JETZT ONLINE BESTELLEN
First Edition Juli 2008
ISBN 978-0-596-51978-0
222 Seiten
EUR32.00
Weitere Informationen zu diesem Buch
Inhaltsverzeichnis |
Rezensionen |
Inhaltsverzeichnis
- Chapter 1: Introduction
- Inhaltsvorschau
Someone who is more productive performs more effective work in a given time interval than someone less productive. This book is all about how to become more productive as you go about the tasks required to develop software. It is language and operating system agnostic: I provide tips in a variety of languages, and across three major operating systems: Windows (in various flavors), Mac OS X, and *-nix (Unix and Linux alternatives).This book is about individual programmer productivity, not group productivity. To that end, I don’t talk about methodology (well, maybe a little here and there, but always on the periphery). I also don’t discuss productivity gains that affect the whole team. My mission is to allow individual programmers the tools and philosophies to get more useful work done per unit of time.I work for ThoughtWorks, an international consulting company of about 1,000 employees spread across 6 countries. Because we are traveling consultants (especially in the U.S.), we are a demographically very young company. It came to pass at one of our company outings (where beverages were served) that I starting chatting with one of the People people. She asked me how old I was and I told her. Then, she gave me an off-handed compliment(?): “Wow, you’re old enough to add diversity to the company!” That sparked some thoughts. I’ve been developing software for many years (cue the maudlin “Back in my day, we had kerosene-powered computers…”). During that time, I’ve observed an interesting phenomenon: developers are getting less efficient, not more. Back in ancient times (a couple of decades in computer time), running a computer was a difficult job, much less programming the thing. You had to be a really clever developer to get anything useful from the beastly machine. This crucible forged Really Smart Guys who developed all sorts of efficient ways to interact with the intractable computers of their age.Slowly, due to the hard work of programmers, computers became easier to use. This innovation was really to stop users from complaining so much. The Really Smart Guys congratulated themselves (as all programmers do when they can get a user to pipe down). Then a funny thing happened: a whole generation of developers came along who no longer needed clever tricks and devious intelligence to get computers to do what they wanted. The developers, like the end users, basked in the easier-to-use computers. So, what’s wrong with that? After all, productivity is a good thing, right?Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Why a Book on Programmer Productivity?
- InhaltsvorschauI work for ThoughtWorks, an international consulting company of about 1,000 employees spread across 6 countries. Because we are traveling consultants (especially in the U.S.), we are a demographically very young company. It came to pass at one of our company outings (where beverages were served) that I starting chatting with one of the People people. She asked me how old I was and I told her. Then, she gave me an off-handed compliment(?): “Wow, you’re old enough to add diversity to the company!” That sparked some thoughts. I’ve been developing software for many years (cue the maudlin “Back in my day, we had kerosene-powered computers…”). During that time, I’ve observed an interesting phenomenon: developers are getting less efficient, not more. Back in ancient times (a couple of decades in computer time), running a computer was a difficult job, much less programming the thing. You had to be a really clever developer to get anything useful from the beastly machine. This crucible forged Really Smart Guys who developed all sorts of efficient ways to interact with the intractable computers of their age.Slowly, due to the hard work of programmers, computers became easier to use. This innovation was really to stop users from complaining so much. The Really Smart Guys congratulated themselves (as all programmers do when they can get a user to pipe down). Then a funny thing happened: a whole generation of developers came along who no longer needed clever tricks and devious intelligence to get computers to do what they wanted. The developers, like the end users, basked in the easier-to-use computers. So, what’s wrong with that? After all, productivity is a good thing, right?It depends. What is productive for a user (nice graphical user interface, mice, pull-down menus, etc.) can actually be a hindrance to someone trying to get the best performance from a computer. “Easy to use” and “efficient” overlap infrequently. Developers who grew up using graphical user interfaces (OK, I’ll just go ahead and say it: Windows) don’t know many of the cool, efficient tricks of the trade of the Really Smart Guys of yesteryear. Developers today are notEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- What This Book Is About
- InhaltsvorschauThe Productive Programmer is divided into two parts. The first discusses the mechanics of productivity, and the tools and their uses that make you more productive as you go through the physical activities of developing software. The second part discusses the practice of productivity, and how you can leverage your knowledge and the knowledge of others to produce better software more quickly. In both sections, you will likely run into some things you already know, as well as things you have never thought of before.You can treat this book as a recipe book for command-line and other productivity tips and still reap benefits. But if you understand why something increases productivity, you can recognize it all around you. Creating patterns to describe something creates nomenclature: once you have a name for something, it’s easier to recognize when you see it again. One of the goals of this book is to define a set of productivity principles to help you define your own productivity techniques. Like all patterns, it becomes easier to identify them once they have names. Knowing why something speeds you up allows you to more quickly identify other things that will help you work faster.This is not just a book on how to use computers more effectively (although that is a side effect). It is focused on programmer productivity. To this end, I don’t cover many things that are obvious to casual or even power users (although, as the exception that proves the rule, the earlier section ” does show an obvious tip). Programmers represent a unique subsection of computer users. We should be able to bend computers to our will more effectively than anyone else because we understand the most about how they really work. Mostly, this book is about things you can do with and to a computer to make your job easier, faster, and more efficient. However, I also discuss some low-hanging fruit that can make you more productive.covers every productivity tip I could invent, harvest, bully out of my friends, or read about. Originally, I aimed to create the world’s most awesome collection of productivity recipes. I don’t know if that happened, but you will still find a pretty impressive collection of recipes here.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Where to Go Now?
- InhaltsvorschauThe two parts of this book stand alone, so you can read them in any order; however, is a tad more narrative, and unexpected connections may pop up. Still, most of the material in it is nonsequential: you can read it in any order you like.One note of warning. If you aren’t comfortable with basic command-line stuff (pipes, redirection, etc.), you should make a quick visit to . It covers getting an environment set up that is suitable for using many of the tricks and techniques discussed in . It’s pretty painless, I promise.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 2: Acceleration
- Inhaltsvorschau
You have to boot it up, you have to know how to launch applications, and you must understand the interaction model, which can differ between applications. The less you interact with your computer, the faster you can go. In other words, eliminating ceremony allows you more time to get to the essence of the problem. Time you spend digging through a long filesystem hierarchy to find something is time you could be using to be more productive. Computers are tools, and the more time you spend on the care and feeding of the tool, the less work you get done. The science fiction author Douglas Adams had a great quote: “We are stuck with technology when what we really want is just stuff that works.”Concentrate on essence, not ceremony.This chapter is all about figuring out ways to accelerate your interaction with your computer, whether it’s launching applications more quickly, finding files faster, or using the mouse less.Take a look at your computer’s list of applications. If you are using Windows, click on Start and choose Programs. How many columns do you have? Two? Three? Four!? As hard drives have gotten bigger and the kinds of applications (and therefore the tools we must use) have gotten more complex, the number of applications we use has exploded. Of course, with commonplace 100 GB drives, we can pack lots of stuff into our systems. But volume exacts a price.The usefulness of an application list is inversely proportional to its length.The longer the list, the less useful it becomes. With three columns in Windows or a dock that squeezes items to microscopic size in Mac OS X, it’s getting harder and harder to find what we need. This hits developers particularly hard because we have lots of occasional applications: special-purpose tools that we may run only one day a month, but that we desperately need when that day arrives.Launchers are applications that allow you to type the first part of the name of an application (or document) to launch it. Most of the time, this is a more efficient way to launch applications.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Launching Pad
- InhaltsvorschauTake a look at your computer’s list of applications. If you are using Windows, click on Start and choose Programs. How many columns do you have? Two? Three? Four!? As hard drives have gotten bigger and the kinds of applications (and therefore the tools we must use) have gotten more complex, the number of applications we use has exploded. Of course, with commonplace 100 GB drives, we can pack lots of stuff into our systems. But volume exacts a price.The usefulness of an application list is inversely proportional to its length.The longer the list, the less useful it becomes. With three columns in Windows or a dock that squeezes items to microscopic size in Mac OS X, it’s getting harder and harder to find what we need. This hits developers particularly hard because we have lots of occasional applications: special-purpose tools that we may run only one day a month, but that we desperately need when that day arrives.Launchers are applications that allow you to type the first part of the name of an application (or document) to launch it. Most of the time, this is a more efficient way to launch applications.Eye candy looks good but isn’t nutritious.If you know the name of the thing you are after (like the name of the application), why not just tell your computer what you want, rather than sort through a massive list or search for it in a sea of icons? Launchers cut through the graphical eye candy and drill precisely and quickly to the thing you need.All the major operating systems have open source and free launchers that allow you to type the name (or a portion of the name) of the application you want to launch. A few that are worth trying are Launchy, Colibri, and Enso. Both Launchy and Colibri are open source and therefore free, and they both allow you to open a small window and start typing the name of your application, which will pop up on a list. Launchy is currently the most popular of the open source launchers. Colibri is attempting to reproduce a Mac OS X utility called Quicksilver (discussed in the upcoming section ”).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Accelerators
- InhaltsvorschauPrefer typing over mousing.Developers are essentially specialized data entry clerks. Instead of coming from some external source, the data we enter into the computer comes from our heads. But the lessons of data entry operators still resonate. Data entry workers who are paid for the amount of information they can input know that using the mouse slows them down by orders of magnitude. Developers can learn an important lesson here.The classic “No Mouse Required” application is the VI editor. Watching an experienced VI user inspires awe. The cursor seems to follow their eyes. Unfortunately, it takes about two years of daily VI usage to get to that point because the learning curve is so daunting. If you’ve used it every day for 1 year and 364 days, you’ll still struggle. The other classic Unix editor is Emacs, which is also very keyboard-centric. Emacs, though, is the proto-IDE: through its plug-in architecture, it does a lot more than just edit files. VI users scornfully refer to Emacs as “a great operating system with limited text-editing capabilities.”Both VI and Emacs support a very important accelerator: never taking your hands off the character keys. Even reaching down to the arrow keys on a keyboard slows you down because you must return to the home row keys to type characters again. Really useful editors keep your hands in optimum position to input and navigate at the same time.Short of learning VI, you can figure out how to use accelerators to speed up your interaction with both the operating system and its applications. This section describes some ways to accelerate your use of both underlying operating systems and tools like IDEs. I start at the operating system–level and work my way up the food chain toward IDEs.Graphical operating systems favor convenience (and eye candy) over raw efficiency. The command line is still the most efficient way to interact with a computer because very little stands between the user and the desired result. Still, most modern operating systems support a plethora of keyboard shortcuts and other navigation aids to help speed up your work.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Macros
- InhaltsvorschauMacros are recorded snippets of interaction with your computer. Generally, each tool comes with its own macro recorder (because only the tool knows how it processes keys). That means, of course, that there is no standard macro syntax, sometimes even between different versions of a product. For years, Microsoft Word and Excel had very different macro syntax even though they were from the same company and in the same Office suite. It wasn’t until Office 2000 that Microsoft finally agreed on a single syntax. Even though a Tower of Babel exists between tools, macros can still help solve very specific problems that you face on a daily basis.For any symmetric operation on multiple lines of text, find a pattern and record a macro.How often do you find yourself working in a pattern? You’ve cut and pasted some information from an XML document, and now you have to remove all the XML cruft from around the real data inside to clean it up. Macros used to be all the rage with developers, but they seem to have fallen out of favor recently. I suspect that the live template feature of most modern IDEs has removed some of the need for macros.But, however much you lean on live templates, uses still exist for recording macros. The common scenario is the one highlighted earlier: doing one-time processing on some information to either de-cruft it from another format or cruft it up for the consumption of some other tool. If you can shift your perspective on the task and see it as a series of repeatable steps, you’ll find macros can be used for lots of chores.The more times you perform a particular operation on a chunk of text, the greater the likelihood you’ll do it again.Even if you use Eclipse (which doesn’t have a macro recorder), you can always jump over to a text editor and use its macro recorder for just this chore. One of the important selection criteria for a text editor is its macro recording facilities and the format of the recorded macros. It is nicer if the macros produce some kind of readable code that you can tweak by hand, creating a reusable asset you can use later down the road. After all, if you’ve cut and pasted some stuff from one format to another once, chances are good you’ll have to do it again.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Summary
- InhaltsvorschauIt’s one thing to talk about accelerating your computer interactions with launchers, clipboard managers, IDE shortcuts, and all the various things mentioned in this chapter. It’s another to apply them. You know what speeds up your work, but you feel like you don’t have time to apply it. “I know there’s a keyboard shortcut that does this, but I’m in a hurry, so I’ll use the mouse instead and look up that keyboard shortcut later.” Later never comes. Becoming more productive involves finding a balance between pursuing productivity without gutting your current productivity (I know, this drips with irony). Try to tackle one productivity enhancer per week, concentrate on just that one until it becomes ingrained, then move on to the next. This method will have very little impact on your time, and you’ll gradually accrue better productivity.Spend a little time each day to make every day more productive.Applying acceleration has two contexts: knowledge of accelerators and the proper context in which to use them. For example, install a clipboard utility on your machine and force yourself to think about it every time you need to copy and paste something. You will gradually start seeing situations where it saves you time because you are harvesting a set of copied values all at once, then pasting them as a group as well. Once you’ve internalized that utility, move on to another one. It’s all about finding the balance of spending time to learn to become more productive.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 3: Focus
- Inhaltsvorschau
inefficiencies and needless distractions. You probably suffer from lots of distractions at work, both from the computer itself and from the outside world. Here you will learn how to enhance your focus with specific tools and approaches to interacting with your computer, as well as ways to make your coworkers leave you alone so that you can quit banging rocks together and get some work done. The goal is to get you back to that dazed but happy state of just having scaled a virtual mountain.You are a knowledge worker, meaning you are paid for the creative and innovative ideas you produce. Dealing with constant distractions, both at your desk and on your desktop, can threaten your best contributions to your projects. Developers crave a state known as flow, discussed in lots of places (it even has an entire book devoted to it, written by Csikszentmihalyi). All developers know this state: it’s when you are so focused that time disappears, you develop an almost symbiotic relationship with the machine and the problem you are attacking. This is the state you’ve been in when you say, “Wow, have four hours passed? I didn’t even notice.” The problem with flow is that it is fragile. One distraction pulls you out, and it takes effort to get back in. It also suffers from inertia. Late in the day, you have to fight harder to get back to that state, and the more times you are abruptly pulled out, the harder it becomes. Distractions kill your focus on the problem at hand, making you less productive. Fortunately, you can effectively block distractions in a few simple ways.The higher the level of concentration, the denser the ideas.Concentration is hard to maintain, especially when your computer seems determined to drag your attention away from your work. Blocking visual and audible distractions helps you maintain a good, focused flow state. For audible distractions (especially if you don’t have an office with a door you can close), you can wear headphones (even if you aren’t listening to music). When others see you wearing headphones, they are less likely to interrupt you. If your office won’t allow headphones, consider putting a “Do Not Disturb” sign on the entryway to your cubicle. That should make people think twice before barging in.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Kill Distractions
- InhaltsvorschauYou are a knowledge worker, meaning you are paid for the creative and innovative ideas you produce. Dealing with constant distractions, both at your desk and on your desktop, can threaten your best contributions to your projects. Developers crave a state known as flow, discussed in lots of places (it even has an entire book devoted to it, written by Csikszentmihalyi). All developers know this state: it’s when you are so focused that time disappears, you develop an almost symbiotic relationship with the machine and the problem you are attacking. This is the state you’ve been in when you say, “Wow, have four hours passed? I didn’t even notice.” The problem with flow is that it is fragile. One distraction pulls you out, and it takes effort to get back in. It also suffers from inertia. Late in the day, you have to fight harder to get back to that state, and the more times you are abruptly pulled out, the harder it becomes. Distractions kill your focus on the problem at hand, making you less productive. Fortunately, you can effectively block distractions in a few simple ways.The higher the level of concentration, the denser the ideas.Concentration is hard to maintain, especially when your computer seems determined to drag your attention away from your work. Blocking visual and audible distractions helps you maintain a good, focused flow state. For audible distractions (especially if you don’t have an office with a door you can close), you can wear headphones (even if you aren’t listening to music). When others see you wearing headphones, they are less likely to interrupt you. If your office won’t allow headphones, consider putting a “Do Not Disturb” sign on the entryway to your cubicle. That should make people think twice before barging in.For visual distractions, turn off all the things on your machine that break your concentration. Email notification is very damaging because it creates artificial urgency. How many of the emails you receive in the course of a day really require an immediate response? Turn off your email client and check mail in batches, when you reach a natural breaking point in your work. This allows you to determine when you want to break your train of thought.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Search Trumps Navigation
- InhaltsvorschauThe bigger the haystack, the harder it is to find the needle.Projects have gotten bigger and bigger, along with the packages and namespaces that go along with them. Hierarchies are hard to navigate when they get big: they’re too deep. Filesystems that work well with 200 MB of storage suffer when they reach 200 GB. Filesystems have become massive haystacks, and we constantly do hard target searches for needles. Taking time out to dig around for files pulls you away from the problem upon which you should be focusing. Fortunately, new search tools help you dispense with cumbersome filesystem navigation almost entirely.Recently, powerful search applications appeared at the operating system–level: Spotlight in Mac OS X and Windows Search in Vista. These searching applications are different from the quaint search features in previous versions of Windows (whose only real use was to show an animation of a dog). This new breed of search tools indexes the interesting parts of your entire hard drive, making searches blazingly fast. They don’t just look at filenames: they index the contents of files.Several desktop search add-ons exist for pre-Vista Windows. My current favorite is the free Google Desktop Search. Out of the box, it searches only “normal” files (like spreadsheets, Word documents, email, etc.). One of the best parts of Google Desktop Search is its plug-in API, which allows developers to add search plug-ins. For example, Larry’s Any Text File Indexer allows you to configure Google Desktop Search for source files.Once you’ve installed Larry's Any Text File Indexer and allowed it to index your hard drive (which it does in the background, during idle times), you can search for a snippet of the contents of a file. For example, in Java, the name of the file must match the name of the public class. In most other languages (like C#, Ruby, Python), the filename conventionally matches the class name. Or, if you are looking for all the files that use a particular class, you can search for code fragments you know exist. For example:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Find Hard Targets
- InhaltsvorschauTry simple searching before resorting to “hard target” searching.Google Desktop Search, Spotlight, and Vista’s search are great for finding files when you know some of the content. But sometimes you want more sophisticated searching capabilities. None of the aforementioned tools supports regular expressions, which is a shame because regular expressions have been around a long time and offer an incredibly powerful search mechanism. The more efficiently you can find something, the sooner you can return your focus to the problem at hand.All flavors of Unix (including Mac OS X, Linux, and even Cygwin in Windows) include a utility called find. Find is responsible for finding files from the given directory downward, recursing through directory structures. Find takes tons of parameters that allow you to refine your searches, including regular expressions for filenames. For example, here is a find invocation that locates all Java sources files with “Db” right before the file extension:
-regex
This search tells find to start from the current directory (the “.”) and find all files that have zero or more characters (the “.*”) before the string “Db”, which comes before the “.” (which must be escaped because “.” normally means “any single character”), followed by the file extension of Java.find is pretty darn useful by itself, but when you combine it with grep, you have a really powerful team. One of the options on find is -exec, which executes the command(s) that follow with the option of passing the found filename as a parameter. In other words, find will find all the files that match your criteria and then pass each file (as it’s discovered) to the command to the right of -exec. Consider this command (explained in ):-name \
Table : Decoding the find command Character(s) What it’s doing findExecute the find command. .From the current directory. -nameMatch the name of “*.java” (note that this isn’t a regular expression, it’s a filesystem “glob,” where the Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Rooted Views
- InhaltsvorschauA rooted view is a view of a directory structure rooted at a particular subdirectory, where you see only the contents from the root directory downward. If you are working on a particular project, you don’t care about the files from other projects. Rooted views allow you to eliminate the distraction of out-of-context files and focus just on the set of files upon which you need to work right now. All the major platforms support this concept, but the implementations are different.A rooted Explorer view (“rooted” on the c:\work\sample code\art\art_emotherearth_memento folder) appears in .
Figure : Rooted views in WindowsThis is a normal Explorer window, opened with the following parameters:explorer /e,/root,c:\work\cit
The rooted view affects only this instance of Explorer. If you launch another Explorer window using typical means, you’ll see a normal instance of Explorer. To take advantage of rooted views, create shortcuts to Explorer with the rooted view parameters above. Rooted views work in all versions of Windows, from Windows 95 through Windows Vista.Rooted views turn Explorer into a project management tool.Rooted views are particularly good for project work and especially good if you use a file- or folder-based version control system (like Subversion or CVS). As far as the rooted instance of Explorer is concerned, your project files and folders make up the entire universe. You can access the plug-in Tortoise (a Subversion management tool for Explorer) via any place you click within the rooted view. And, more importantly, you eliminate the distractions created by a bunch of folders and files that mean nothing to the project on which you are working.Root views work a little differently in Mac OS X. While you can’t create a Finder view that is uniquely focused on a single directory structure like you can with Windows Explorer, you can still create specialized rooted views that cut through the massive directory structures. In Finder, you can create shortcuts to directories by dragging the directory in question to the sidebar or to the dock. This allows you to open that directory right from Finder, as shown in .Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Sticky Attributes
- InhaltsvorschauThe command line in Windows has a nasty surprise turned on by default called Quick Edit Mode. This is a switch set in the properties of the command window that allows you to select text to copy with the mouse. Any click inside the window starts a drag operation, highlighting a rectangular section of text, ready to copy it to the clipboard (oddly enough, the standard keyboard shortcut doesn’t work here; you have to hit the key instead). Therein lies the problem. Because you are selecting text in the console window, it freezes all activity (that is, all the processes and threads occupying that window) as soon as you start dragging the mouse. This makes sense: it would be annoying to try to copy something that actively scrolls out of your way. Typically, in a windowed environment, it is perfectly safe to click anywhere in a window to give it focus. But if you click in a command prompt window, you’ve inadvertently started a drag operation, which freezes all the processes. Ironically, setting focus to a window can accidentally destroy the focus on the job at hand because you start wondering “Why is nothing happening in that window?” You can fix this “feature” using sticky attributes.Take advantage of built-in focus facilities (like colors).Windows keeps track of customized settings in command prompts via the title of the window. When you close a command prompt in Windows, it asks if you want to save these settings for all command prompts with the same title (thus making them “sticky”). You can leverage that to create specialized command prompts. Create a shortcut to launch a window with a specific title, set some options, and save the options for the window as you close it. For development work, you need a command prompt with the following characteristics:
- Almost infinite scrolling. The default is a measly 300 lines, which will easily scroll off when doing something interesting. Set it to 9999 lines (and ignore the 1990s-era warning that your command prompt will now eat a precious 2 MB of memory).
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Project-Based Shortcuts
- InhaltsvorschauAll the major operating systems have some alias, link, or shortcut mechanism. Use it to create project management workspaces. Frequently, you have projects that have documents strewn all over your hard drive: requirements/use cases/story cards in one place, source code in another, database definitions in another. Navigating between all those folders is a waste of time. Instead of forcing all your files for a project into a single location, you can put them all together virtually. Make one project-based folder that has shortcuts and links for the entire project. You’ll find that you spend much less time spelunking around the filesystem.Use links to create virtual project management folders.Place your project management folder under one of the Quick Launch buttons in Windows or on the dock in Mac OS X. These two areas don’t support a large number of items, but using them for just a few project consolidator folders makes sense.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Multiply Your Monitors
- InhaltsvorschauMonitors have gotten cheap, and developers can use the extra real estate. It is penny-wise and dollar foolish not to supply developers with ultra-fast computers and dual monitors. Every moment that a knowledge worker stares at an hourglass is pure wasted productivity. Having to work to manage all the overlapping windows on a cramped monitor also wastes time.Multiple monitors allow you to write code on one and debug on the other. Or keep documentation alongside your coding. Having multiple monitors is just the first step, though, because you can also segregate your dual workspaces into a bunch of specialized views using virtual desktops.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Segregate Your Workspace with Virtual Desktops
- InhaltsvorschauVirtual desktops unclutter your stacks of windows.One of the cool features from the Unix world is virtual desktops. A virtual desktop is just like your regular desktop, with windows in a certain arrangement, but the “virtual” part indicates that you can have more than one. Instead of having one massively jumbled desktop with your IDE, your database console, and all your email, instant messaging, browsers, etc. on it, you can have singly purposed desktops for each logical grouping of activities. A massive pile of windows on your desktop distracts you from your focus because you constantly have to sort through the windows.Virtual desktops used to exist solely on high-end Unix workstations (which had the graphical horsepower to support such a thing). But now they exist on all the major platforms. On Linux, both GNOME and KDE have virtual desktops built-in.The Leopard version of Mac OS X (version 10.5) added this feature, called Spaces. But previous Mac OS X users aren’t left out: several open source and commercial virtual desktops exist, such as VirtueDesktops. It offers sophisticated features such as “pinning” applications to a certain desktop (meaning that application will appear only on that desktop, and the focus will change to that desktop if you select that application). This is a great feature for developers, who typically set up certain desktops for specific purposes (development, documentation, debugging, etc.).One of my recent projects was a Ruby on Rails gig where we were pair-programming on Mac Minis (the little smaller-than-a-breadbox machines that you buy without a monitor or keyboard). They make surprisingly good development machines, especially with two keyboards, mice, and monitors. What made them great environments, though, were the virtual desktops. We had each machine set up the same way (to preserve sanity as we swapped pairs), with all the development tools on one desktop, documentation on another, and the running application (a terminal window running the web server in debug mode and browser) on a third. Each desktop was completely self-contained, and as we switched applications, the appropriate desktop rotated into view. The environment allowed us to leave all the windows for a particular desktop in the same place all the time, with very little tiling and cascading. In my last gig, I was coding alone on Windows, but I still set up “communication,” “documentation,” and “development” desktops, which cut down on clutter and aided my sanity.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Summary
- InhaltsvorschauThis chapter covered several different aspects of focus: finding ways to modify your environment to reduce distractions, ways to make your computer less distracting, and tools to enhance your focus. Hopefully, you can now see why I decided to organize the topics around the ideas of productivity principles: these topics would seem unrelated to one another without the unifying element of focus.Focus in modern environments is hard to achieve. Yet, to fully realize your potential, you must find a way within your specific circumstances to carve out a workable space and environment. Doing so will ultimately greatly enhance your productivity.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 4: Automation
- Inhaltsvorschau
. I wanted to open Excel with multiple worksheets, but doing it by hand was cumbersome (and Excel doesn’t allow you to pass multiple files on a command line). So, I took a few minutes to write the following little Ruby script:= docs = .new docs << docs << docs << excel = .new() workbooks = excel.WorkBooks excel.Visible = doc_list.each || workbooks.Open( + f, ) puts , + f excel.Windows.Arrange() .daily_logsEven though it didn’t take long to open the files by hand, the little time it took was still a waste of time, so I automated it. Along the way, I discovered that you can use Ruby on Windows to drive COM objects, like Excel.Computers are designed to perform simple, repetitive tasks over and over really fast. Yet an odd thing is happening: people are performing simple, repetitive tasks by hand on computers. And the computers get together late at night and make fun of their users. How did this happen?Graphical environments are designed to help novices. Microsoft created the Start button in Windows because users had a hard time in previous versions knowing what to do first. (Oddly, you also shut down the computer with the Start button.) But the very things that make casual users more productive can hamper power users. You can get more done at the command line for most development chores than you can through a graphical user interface. One of the great ironies of the last couple of decades is that power users have gotten slower at performing routine tasks. The typical Unix guys of yore were much more efficient because they automated everything.If you’ve ever been to an experienced woodworker’s shop, you’ve seen lots of specialized tools lying around (you may not have even realized that a laser-guided, gyroscopically balanced lathe existed). Yet in the course of most projects, woodworkers use a little scrap of wood from the floor to temporarily hold two things apart or hold two things together. In engineering terms, these little scraps are “jigs” or “shims.” As developers, we create too few of these little throwaway tools, frequently, because, we don’t think of tools in this way.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Don’t Reinvent Wheels
- InhaltsvorschauGeneral infrastructure setup is something you have to do for every project: setting up version control, continuous integration, user IDs, etc. Buildix is an open source project (developed by ThoughtWorks) that greatly simplifies this process for Java-based projects. Many Linux distributions come with a “Live CD” option, allowing you to try out a version of Linux right off the CD. Buildix works the same way, but with preconfigured project infrastructure. It is itself an Ubuntu Live CD, but with software development goodies preinstalled. Buildix includes the following preconfigured infrastructure:
- Subversion, the popular open source version control package
- CruiseControl, an open source continuous integration server
- Trac, open source bug tracking and wiki
- Mingle, ThoughtWorks’ agile project tracking tool
You boot from the Buildix CD and you have project infrastructure. Or, you can use the Live CD as an installation CD for an existing Ubuntu system. It’s a project in a box.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Cache Stuff Locally
- InhaltsvorschauWhen you develop software, you constantly refer to resources on the Internet. No matter how fast your network connection, you still pay a speed penalty when you view pages via the Web. For oft-referenced material (like programming APIs), you should cache the content locally (which also lets you access it on airplanes). Some content is easy to cache locally: just use your browser’s “Save Page” feature. Lots of times, however, caching gets an incomplete set of web pages.wget is a *-nix utility designed to cache parts of the web locally. It is available on all the *-nixes and as part of Cygwin on Windows. wget has lots of options to fetch pages. The most common is mirror, which mirrors the entire site locally. For example, to effectively mirror a web site, issue the following command:
wget â~@~Sw â~@~S-convert-links â~@~SP c:\wget_files\example1That’s a mouthful. gives a breakdown.Table : Using the wget command Character(s) What it’s doing wgetThe command itself. --mirrorThe command to mirror the web site. wget will recursively follow links on the site and download all necessary files. By default, it only gets files that were updated since the last mirror operation to avoid useless work. --html-extensionLots of web files have non-HTML extensions even if they ultimately yield HTML files (like cgi or PHP). This flag tells wget to convert those files to HTML extensions. --convert-linksAll links on the page are converted to local links, fixing the problem for a page that has absolute URIs in them. wget converts all the links to local resources. -P c:\wget_files\example1The target directory where you want the site placed locally. Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Automate Your Interaction with Web Sites
- InhaltsvorschauThere may be web sites from which you would like to distill information that require logon or other steps to get to content. cURL allows you to automate that interaction. cURL is another open source tool that is available for all the major operating systems. It is similar to wget but specializes in interacting with pages to retrieve content or grab resources. For example, say you have the following web form:cURL allows you to fetch the page that results after supplying the two parameters:
curl
You can also interact with pages that require an HTML POST instead of GET using the “-d” command-line option:curl www.hotmail.com/when/junk.cgi
cURL’s real sweet spot is interacting with secured sites via a variety of protocols (like HTTPS). The cURL web site goes into incredible detail on this subject. This ability to navigate security protocols and other web realities makes cURL a great tool to interact with sites. It comes by default on Mac OS X and most Linux distributions; you can download a copy for Windows at http://www.curl.org.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Interact with Feeds
- InhaltsvorschauYahoo! has a service (currently and perpetually in beta) called Pipes. The Pipes service allows you to manipulate RSS feeds (like blogs), combining, filtering, and processing the results to create either a web page result or another RSS feed. It uses a web-based drag-and-drop interface to create “pipes” from one feed to another, borrowing the Unix command-line pipe metaphor. From a usability standpoint, it looks much like Mac OS X Automator, where each of the commands (or pipe stages) produces output to be consumed by the next pipe in line.For example, the pipe shown in fetches the blog aggregator from the No Fluff, Just Stuff conference site that includes recent blog postings. The blog postings occur in the form of “blog author - blog title,” but I want only the author in the output, so I use the regex pipe to replace the author-title with just the author’s name.
Figure : Regex pipes in actionThe output of the pipe is either another HTML page or another RSS feed (the pipe is executed whenever you refresh the feed).RSS is an increasingly popular format for developer information, and Yahoo! Pipes allows you to programmatically manipulate it to refine the results. Not only that, Pipes is gradually adding support for harvesting information off web pages to put into pipes, allowing you to automate retrieval of all sorts of web-based information.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Subvert Ant for Non-Build Tasks
- InhaltsvorschauUse tools out of their original context when appropriate.Batch files and bash scripts allow you to automate work at the operating system–level. But both have picky syntax and sometimes clumsy commands. For example, if you need to perform an operation on a large number of files, it is difficult to retrieve just the list of files you want using the primitive commands in batch files and bash scripts. Why not use tools already designed for this purpose?The typical make commands we use now as development tools already know how to grab lists of files, filter them, and perform operations on them. Ant, Nant, and Rake have much friendlier syntax than batch and script files for tasks that must operate on groups of files.Here is an example of subverting Ant to do some work that would be so difficult in a batch file that I never would have bothered. I used to teach a lot of programming classes where I wrote samples on the fly. Frequently, because of questions, I would customize the application as we went along. At the end of the week, everyone wanted a copy of the custom applications I had written. But in the course of writing them, lots of extra stuff piled up (output files, JAR files, temporary files, etc.), so I had to clean out all of the extraneous files and create a nice ZIP archive for them. Rather than doing this by hand, I created an Ant file do to it. The nice part about using Ant was a built-in awareness of a set of files:
== == = = = = = = = = = = = == == =Using Ant allowed me to write a high-level task to perform all the steps I was doing by hand before:== = = ====Writing this as a batch file would have been a nightmare! Even writing it in Java would be cumbersome: Java has no built-in awareness of a set of files matching patterns. When using build tools, you don’t have to create a method or any of the other infrastructure already supplied by the build tools.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Subvert Rake for Common Tasks
- InhaltsvorschauRake is the make utility for Ruby (written in Ruby). Rake makes a great shell script substitute because it gives you the full expressive power of Ruby but allows you to easily interact with the operating system.Here’s an example that I use all the time. I do lots of presentations at developers’ conferences, which means that I have lots of slide decks and corresponding example code. For a long time, I would launch the presentation, then remember all the other tools and samples I would have to launch. Inevitably, I would forget one and have to fumble around during the talk to find the missing sample. Then I wised up and automated the process:
.dirname() + = .dirname() = [ , , , , ] = [ , , ]
This rake file lists all the files that I need to open and all the applications required for the talk. One of the nice things about Rake is its ability to use Ruby files as helpers. This rake file is essentially just declarations. The actual work is done in a base rake file called base, which all the individual rake files rely upon..dirname() + .dirname() + task .new(, ).open_everything
Notice at the top of the file I require a file named talks_helper:, (openers, processes) , = openers, processes .each { || f.gsub , } .nil? .each || pid = {system p} .detach(pid) .nil?This helper class includes the code that does the actual work. This mechanism allows me to have one simple rake file per presentation and automatically launch what I need. Rake’s great advantage lies in the ease with which you can interact with the underlying operating system. When you delimit strings with the backtick character (`), it automatically executes it as a shell command. The line of code that includes really executes the open command from the underlying operating system (in this case, Mac OS X; you can substitute start in Windows), using the variable I have defined above as the argument. Using Ruby to drive the underlying operating system is much easier than writing bash scripts or batch files.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Subvert Selenium to Walk Web Pages
- InhaltsvorschauSelenium is an open source user acceptance testing tool for web applications. It allows you to simulate user actions by automating the browser via JavaScript. Selenium is written entirely in browser technology, so it runs in all mainstream browsers. It is an incredibly useful tool for testing web applications, regardless of the technology used to create the web application.But I’m not here to talk about using Selenium as a testing tool. One of the ancillary projects to Selenium is a Firefox browser plug-in called Selenium IDE. Selenium IDE allows you to record your interaction with a web application as a Selenium script, which you can play back through Selenium’s TestRunner or through Selenium IDE itself. While this is useful when creating tests, it is invaluable if you need to automate your interaction with a web application.Here is a common scenario. You are building the fourth page of a wizard-style web application. The first three pages are complete, meaning that all their interaction works correctly (including things like validations). To debug the behavior of the fourth page, you must walk through the first three pages over and over. And over. And over. You always think, “OK, this will be the last time I have to walk through these pages because I’m sure I’ve fixed the bug this time.” But it’s never the last time! This is why your test database has lots of entries for Fred Flintstone, Homer Simpson, and that ASDF guy.Use Selenium IDE to do the walking for you. The first time you need to walk through the application to get to the fourth page, record it using Selenium IDE, which will look a lot like . Now, the next time you have to walk to the fourth page, with valid values in each field, just play back the Selenium script.
Figure : Selenium IDE with a script ready to runAnother great developer use for Selenium pops up as well. When your QA department finds a bug, they generally have some primitive way of reporting how the bug came about: a partial list of what they did, a fuzzy screen shot, or something similarly unhelpful. Have them record their bug discovery missions with Selenium IDE and report back to you. Then, you can automatically walk through the exact scenario they did, over and over, until you fix the bug. This saves both time and frustration. Selenium has essentially created an executable description of user interaction with a web application. Use it!Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Bash to Harvest Exception Counts
- InhaltsvorschauHere’s an example of using bash that you might run into on a typical project. I was on a large Java project that had gone on for six years (I was just a tourist on this project, arriving in the sixth year for about eight months). One of my chores was to clean up some of the exceptions that occurred on a regular basis. The first thing I did was ask “What exceptions are being thrown and at what frequency?” Of course, no one knew, so my first task was to answer that question.The problem was that this application excreted 2 GB logs each and every week, containing the exceptions I needed to categorize, along with a huge amount of other noise. It didn’t take me long to realize that it was a waste of time to crack open this file with a text editor. So, I sat down for a little while and ended up with this:
X log_week.txtshows what this handy little bash script does.Table : Complex bash command to harvest exception counts Character(s) What it’s doing egrep -oFind all strings in the log file that have some text before “Exception” , sort them, and get a distinct list "[A-Z]\w*Exception"The pattern that defines what an exception looks like log_week.txtThe gargantuan log file | sortPipe the results through sort, creating a sorted list of the exceptions | uniqEliminate duplicate exceptions for X in $(. . .) ;Perform the code in the loop for each exception in the list generated above echo -n -e "processing $X\t"Echo to the console which exception I’m harvesting (so I can tell it’s working) grep -c "$X" log_week.txtFind the count of this exception in the giant log file They still use this little utility on the project. It’s a good example of automating the creation of a valuable piece of project information that no one had taken the time to do before. Instead of wondering and speculating about the types of exceptions being thrown, we could look and find out exactly, making our targeted fixing of the exceptions much easier.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Replace Batch Files with Windows Power Shell
- InhaltsvorschauAs part of the work on the Vista release of Windows, Microsoft significantly upgraded the batch language. The code name was Monad, but when it shipped it became Windows Power Shell. (For the sake of the extra trees required to spell it out every time, I’m going to keep calling it “Monad.”) It is built-in to Windows Vista, but you can also use it on Windows XP by just downloading it from the Microsoft web site.Monad borrows much of its philosophy from similar command shell languages like bash and DOS, where you can pipe the output of one command into another. The big difference is that Monad doesn’t use plain text (like bash); instead, it uses objects. Monad commands (called cmdlets) understand a common set of objects that represent operating system constructs, like files, directories, even things like the Windows event viewer. The semantics of using it work the same as bash (the pipe operator is even the same old symbol), but the capabilities are vast.Here’s an example. Say that you want to copy all the files that were updated since December 1, 2006 to a folder named DestFolder. The Monad command looks like this:
dir | where-object { $_.LastWriteTime -gt "12/1/2006" } | move-item -destination c:\DestFolderBecause Monad cmdlets “understand” other cmdlets and the kinds of things they output, you can write scripts much more succinctly than with other scripting languages. Here’s an example. Let’s say that you needed to kill all processes using more than 15 MB of memory using bash:ps -el | awk '{ if ( $6 > (1024*15)) { print $3 } }' | grep -v PID | xargs killPretty ugly! It uses five different bash commands, including awk to parse out the results of the ps command. Here is the equivalent Monad command:get-process | where { $_.VS -gt 15M } | stop-processHere, you can use the where command to filter the get-process output for a certain property (in this case, the VS property, which is the memory size).Monad was written using .NET, which means that you also have access to standard .NET types. String manipulation, which has traditionally been tough in command shells, relies on the methods in .NET. For example, issuing the following Monad command:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Mac OS X Automator to Delete Old Downloads
- InhaltsvorschauMac OS X has a graphical way of writing batch files called Automator. In many ways, it is a graphical version of Monad even though it predates Monad by several years. To create Automator workflows (Mac OS X’s version of a script), drag commands from Automator’s work area and “wire” together the output of one command and the input of another. Each application registers its capabilities with Automator upon installation. You can also write pieces of Automator in ObjectiveC (the underlying development language of Mac OS X) to extend it.Here is an example Automator workflow that deletes all the old files you’ve downloaded if they are more than two weeks old. The workflow is shown in and consists of the following steps:
- This workflow caches the last two weeks of downloads in a folder called recent.
- Empty recent so that it is ready for new files.
- Find all downloads with a modified date within the last two weeks.
- Move them to recent.
- Find all the non-folders in the downloads directory.
- Delete all the files.
Figure : “Delete old downloads” workflow in Mac OS X AutomatorThis workflow does more work than the Monad script above because there is no easy way in the workflow to specify that you want all the files not modified in the last two weeks. The best solution is to grab the ones that have been modified in the last two weeks, move them out of the way to a cache directory (named recent), and delete all the files in downloads. You would never bother to do this by hand, but because it’s an automated utility, it can do extra work. One alternative would be to write a shell script in bash and incorporate it into the workflow (one of the options is to call a bash script), but then you’re back to parsing out the results of a shell script to harvest the names. If you wanted to go that far, you could do the whole thing as a shell script.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Tame Command-Line Subversion
- InhaltsvorschauEventually, you get to the point where you can’t subvert another tool or find an open source project that does just what you want. That means it’s time to build your own little jig or shim. This chapter contains lots of different ways to build tools; here are some examples of using these tools to solve problems on real projects.I’m a big fan of the open source version control system Subversion. It is just the right combination of power, simplicity, and ease of use. Subversion is ultimately a command-line version control system, but lots of developers have created frontends for it (my favorite is the Tortoise integration with Windows Explorer). However, the real power of Subversion lies at the command line. Let’s look at an example.I tend to add files in small batches to Subversion. To use the command-line tool, you must specify each of the filenames you want to add. This isn’t bad if you have only a few, but if you’ve added 20 files, it is cumbersome. You can use wildcards, but you’ll likely grab files that are already in version control (which doesn’t hurt anything, but you’ll get piles of error messages that might obscure other error messages). To solve this problem, I wrote a little one-line bash command:
svn st | grep '^\?' | tr '^\?' ' ' | sed 's/[ ]*//' | sed 's/[ ]/\\ /g' | xargs svn add
shows what this one-liner does.Table : Analysis of svnAddNew command sequence Command Result svn stGet Subversion status on all files in this directory and all its subdirectories. The new ones come back with a ? at the beginning and a tab before the filename. grep '^\?'Find all the lines that start with the ?. tr '^\?' ' 'Replace the ? with a space (the tr command translates one character for another). sed 's/[ ]*//'Using sed, the stream-based editor, substitute spaces to nothing for the leading part of the line. sed 's/[ ]/\\ /g'The filenames may have embedded spaces, so use sed again to substitute any remaining spaces with the escaped space character (a space with a \ in front). Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Build a SQL Splitter in Ruby
- InhaltsvorschauA coworker and I were working on a project where we needed to be able to parse a large (38,000-line) legacy SQL file. To make the parsing job easier, we wanted to break the monolithic file into smaller chunks of about 1,000 lines each. We thought very briefly about doing it by hand, but decided that automating it would be better. We thought about trying to do this with sed, but it looked like it would be complicated. We eventually settled on Ruby, and about an hour later, we had this:
= = line_num = file_num = .mkdir() .exists? file = .new( + + file_num.to_s + , ::|::|::, ) done, seen_1k_lines = .readlines().each || file.puts(line) seen_1k_lines = (line_num % == ) seen_1k_lines line_num += done = (line.downcase =~ line.downcase =~ ) != done seen_1k_lines file_num += file = .new( + + file_num.to_s + , ::|::|::, ) done, seen_1k_lines =This little Ruby program reads lines from the original source file until it has read 1,000 lines. Then, it starts looking for lines that have either GO or END on them. Once it finds either of those two strings, it finishes off the current file and starts another one.We calculated that it probably would have taken us about 10 minutes to break this file up via brute force, and it took about an hour to automate it. We eventually had to do it five more times, so we almost reclaimed the time we spent automating it. But that’s not the important point. Performing simple, repetitive tasks by hand makes you dumber, and it steals part of your concentration, which is your most productive asset.Performing simple, repetitive tasks squanders your concentration.Figuring out a clever way to automate the task makes you smarter because you learn something along the way. One of the reasons it took us so long to complete this Ruby program was our unfamiliarity with how Ruby handled low-level file manipulation. Now we know, and we can apply that knowledge to other projects. And, we’ve figured out how to automate part of our project infrastructure, making it more likely that we’ll find other ways to automate simple tasks.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Justifying Automation
- InhaltsvorschauWhen you deploy your application, it takes only three steps: run the “create tables” script on the database, copy the application files to your web server, and update the configuration files for the changes you’ve made to the routing for your application. Simple, easy steps. You have to do this every couple of days. So, what’s the big deal? It takes only about 15 minutes.What if your project lasts eight months? You will have to go through this ritual 64 times (actually, the pace will pick up as you near the finish line and have to deploy it a lot more often). Add it up: 64 times performing this chore × 15 minutes = 960 minutes = 16 hours = 2 work days. Two full work days to do the same thing over and over! And this doesn’t take into account the number of times you accidentally forget to do one of the steps, which costs more time in debugging and repairing. If it takes you less than two days to automate the whole process, then it’s a no-brainer because you get pure time savings back. But what if it takes three days to automate it—is it still worth it?I have encountered some system administrators who write bash scripts for every task they perform. They do this for two reasons. First, if you do it once, you’re almost certainly going to do it again. Bash commands are very terse by design, and it sometimes takes a few minutes even for an experienced developer to get it right. But if you ever have to do that task again, the saved commands save you time. Second, keeping all the nontrivial command-line stuff around in scripts creates living documentation of what you did, and perhaps why you performed some task. Saving everything you do is extreme, but storage is very cheap—much cheaper than the time it takes to recreate something. Perhaps you can compromise: don’t save every single thing you do, but the second time you find yourself doing something, automate it. Chances are excellent that if you do it twice, you’ll end up doing it 100 times.Virtually everyone on *-nix systems creates aliases in their hiddenEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Don’t Shave Yaks
- InhaltsvorschauFinally, don’t allow your automation side project to turn into yak shaving. Yak shaving is part of the official jargon file for computer science. It describes this scenario:
- You want to generate documentation based on your Subversion logs.
- You try to add a Subversion hook only to discover that the Subversion library you have is incompatible and therefore won’t work with your web server.
- You start to update your web server, but realize that the version you need isn’t supported by the patch level of your operating system, so you start to update your operating system.
- The operating system upgrade has a known issue with the disk array the machine uses for backups.
- You download an experimental patch for the disk array that should get it to work with your operating system, which works but causes a problem with the video driver.
At some point, you stop and try to remember what got you started down this road. You come to the realization that you are shaving a yak, and you stop to try to figure out what shaving a yak has to do with generating documentation for Subversion logs.Yak shaving is dangerous because it eats up a lot of time. It also explains why estimating tasks is so often wrong: just how long does it take to fully shave a yak? Always keep in mind what you are trying to achieve, and pull the plug if it starts to spiral out of control.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Summary
- InhaltsvorschauThis chapter contained lots of examples of ways to automate things, but the examples aren’t really the important point. They simply serve to illustrate ways that I and others have figured out to automate common chores. Computers exist to perform simple, repetitive tasks: put them to work! Notice the repetitive stuff that you do on a daily and weekly basis and ask yourself: can I automate this away? Doing so increases the amount of time you can spend working on useful problems, instead of solving the same simple problem over and over. Performing simple tasks by hand robs some of your concentration, so eliminating those little nagging chores frees your precious mindpower for other things.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 5: Canonicality
- Inhaltsvorschau
isn’t working on your machine. This can’t be. It worked just last week on Bob’s machine. You go to Bob’s machine and, sure enough, it works beautifully. But some of the other features that work great on your machine don’t work on Bob’s machine. Now it’s time to panic.Before long, the entire development team is standing around Bob’s machine, trying to figure out why what he’s building is different from what everyone else is building. Right before a major project milestone is the wrong (but inevitable) time for this to happen. It turns out that Bob has a newer version of a particular plug-in in his IDE and it changes the way the application runs in his environment. Of course, installing the same version of the plug-in on Bob’s machine breaks other stuff. You, Bob, and all your coworkers are suffering because you’re running more than one version of something important, which always gets out of sync.A canonical representation refers to the simplest form without loss of information. Canonicality refers to the practice of eliminating duplication. In the seminal book The Pragmatic Programmer (Addison-Wesley), Andrew Hunt and David Thomas lay down the law: “Don’t repeat yourself” (DRY). This three-word sentence has profound effects on software development. Glenn Vanderburg calls repetition “the single most diminishing force in software development.” Presumably, you already agree. How do you achieve canonicality in software development? It’s hard to even notice the problems in lots of situations, especially if non-DRYness (moistness?) is the status quo.This chapter provides examples of how to achieve canonicality. It attacks three common sources of non-DRYness: database object-relational mapping, documentation, and communication. Each of the scenarios discussed here arose from real projects, and in each one, developers figured out ways to stay DRY.One obvious application of canonicality has become commonplace in most development shops: version control, which qualifies as canonicality because the “real” files live in version control. Using version control has the obvious benefits of handling the versioning of your files. But it is also a great backup mechanism, keeping your source code in a safe place, away from single instances on developer machines.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - DRY Version Control
- InhaltsvorschauOne obvious application of canonicality has become commonplace in most development shops: version control, which qualifies as canonicality because the “real” files live in version control. Using version control has the obvious benefits of handling the versioning of your files. But it is also a great backup mechanism, keeping your source code in a safe place, away from single instances on developer machines.I tend to prefer version control systems that don’t lock files but rather merge the contents if more than one developer has made changes (called optimistic revisions). This is a good example of a tool that encourages good and punishes bad behavior. Checking your files into version control early and often encourages you to make small changes. Knowing that you’ll face a merge conflict if you make long-term changes to the file encourages you to check in more often. The tool creates a useful tension, modifying the way you work in subtle but beneficial ways. Good tools are ones that encourage good behavior. Thus, I love the open source Subversion version control system: it is very lightweight, it’s free, and it does just what it is supposed to do and nothing else.While the use of version control is virtually universal, often it is not used to its full potential. Version control can make your project artifacts as DRY as possible. Everything required to build your project should reside in version control. That includes binary files (libraries, frameworks, JAR files, build scripts, etc.). The only things that shouldn’t be in version control are the configuration files particular to a developer’s machine because of paths, IP addresses, etc. Even in this scenario, only the information unique to a developer’s workstation should reside in a local file. Make utilities (like Ant and Nant) allow you to externalize specific information so that you can isolate just the changes.Why keep binaries? Projects today depend on a swath of external tools and libraries. Let’s say you are using one of the popular logging frameworks (like Log4J or Log4Net). If you don’t build the binaries for that logging library as part of your build process, you should keep it in version control. That allows you to continue to build your software even if the framework or library in question disappears (or, more likely, introduces a breaking change in a new version). Always keep the entire universe required to build your software in version control (minus the operating system, and even that is possible with virtualization; see ,” later in this chapter). You can optimize retaining binaries by both keeping them in version control and on a shared network drive. That way, you don’t have to deal with them on an hourly basis, but they are saved in case you need to rebuild something a year later. You never know if you will need to rebuild something. You build it until it works, then forget about it. It is panic inducing to realize you need to rebuild something from two years ago and don’t have all the parts.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Use a Canonical Build Machine
- InhaltsvorschauThe other process required in every development shop is continuous integration. Continuous integration is a process where you build the entire project, run tests, generate documentation, and do all the other activities that make software on a regular basis (the more often the better, generally you should build every time you check in code to version control). Continuous integration is supported by software of the same name. Ideally, the continuous integration server runs on a separate machine, monitoring your check-ins to version control. Every time you perform a code check-in, the continuous integration server springs to life, running a build command that you specify (in a build file like Ant, Nant, Rake, or Make) that usually includes performing a full build, setting up the database for testing, running the entire suite of unit tests, running code analysis, and deploying the application to perform a “smoke test.” The continuous integration server redirects build responsibilities from individual machines and creates a canonical build location.The canonical build machine should not include the development tool you use to create the project, only the libraries and other frameworks needed to build the application. This prevents subtle dependencies on tools from creeping into your build process. Unlike Bob and his hapless coworkers, you want to make sure that everyone builds the same thing. Having a canonical build server makes it the only “official” build for the project. Changes to development tools don’t affect it.Even single developers benefit from having a continuous integration server as the lone build machine. It prevents you from inadvertently allowing tool dependencies from creeping into your project. If you can build your application with a single command on a standalone machine, you obviously have the configuration correct.Numerous continuous integration servers exist, both commercial and open source. CruiseControl is an open source project created by ThoughtWorks, and it has ports to Java, .NET, and Ruby. Other continuous integration servers include Bamboo, Hudson, TeamCity, and LuntBuild.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Indirection
- InhaltsvorschauPlatforms provide structure for heavy items. Development tools create their own platforms by providing stable ground on which you can stand to build software. But part of a platform is infrastructure, and lots of development tools create an infrastructure you can’t control. Indirection allows you to take back control and become more productive.Use indirection to create friendlier workspaces.One of the best things about Eclipse is the rich ecosystem of plug-ins. One of the worst things about Eclipse is that very same rich ecosystem of plug-ins! Different team members download different versions of plug-ins. Generally, this isn’t a problem, but occasionally incompatibilities exist between plug-in versions, and suddenly you have Bob’s nonreproducible build. This represents a canonicality problem.The solution is to make sure that everyone on the project has the exact same set (down to the minor version number) of all plug-ins. The larger the development team, the harder this is to manage. The creators of Eclipse anticipated this problem and allow you to configure multiple plug-in and feature locations. Inexplicably, this option resides on the Help menu, under Software Updates and Manage Configurations. Create a new configuration with the following steps:
- Create a new subdirectory named eclipse. This directory should not reside within Eclipse’s default directory structure.
- Create an empty placeholder file in the new directory named .eclipseextension. In an example of how Windows and Eclipse don’t see eye-to-eye, you cannot create this file in Windows Explorer because it will not allow you to create a file with a “.” as the first character. Thus, you must open a command window (either a Windows shell or bash shell will work) to create this file. The easy way to do this on operating systems that have the command is just .
- Create two (empty) directories in your new directory: features
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Use Virtualization
- InhaltsvorschauUse virtualization to canonicalize dependencies for your projects.Several years ago, I used the indirection aspect of canonicality to streamline development in .NET while trying to recreate a Visual Studio environment that we’d used on another project. There’s a rich ecosystem of third-party components for Visual Studio. The catch is that using third-party components means that every application development environment is subtly different.Client A uses this widget, but you have to make sure not to use it for Client B because they don’t own a license for it. Once you install components on the developer’s machine, they become part of the operating system. Some client setups could take a week just to get the environment tweaked to do work. The problem is one of isolation: you can’t encapsulate the development environment (or the developed application) at any level lower than the operating system.We built our applications using virtual instances of the operating system. The premier tool for doing this at the time was VMWare, and it had just gotten Really Good. We realized that we could take a generic Windows ghost and install all the necessary developer tools on a VMWare image and develop on it. The speed hit at the time wasn’t terrible, and it allowed us clean-room development for each client. When that phase of the project concluded, we saved the VMWare image out to a server.Two years later, when that client came back for enhancements, we started up that application’s development environment just like the day we left it. This approach saved us days of downtime, and made developing for multiple clients a breeze. Client A needs some minor tweaks while I’m working on Client B’s application. No problem—just bounce between virtual machine images. This approach offers other tangible benefits as well. Building a development environment from a clean install of the operating system and developer tools flushes out hidden dependencies between the operating system, tools, office suites, etc.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- DRY Impedance Mismatches
- InhaltsvorschauHave you ever experienced a telephone conversation with an annoying echo? That’s an impedance mismatch, caused when the signals aren’t perfectly synchronized. Impedance mismatch is a term from electrical engineering that has leaked over into the software world because it describes some of our problems.In software, impedance mismatch is one of the common causes of DRY violations. An impedance mismatch occurs at the boundary of two abstraction styles: set-based to object-based, or procedural to object orientation. Because you try to reconcile the two abstraction styles, you end up with repetition around the boundaries.Don’t let object-relational mapping tools (O/R mappers) violate canonicality.One of the constant headaches we face on projects that deal with data is the impedance mismatch between relational databases and object-oriented programming languages. Solving this mismatch has gradually led us to O/R mappers like Hibernate, nHibernate, iBatis, and others. Using an O/R mapper introduces repetition in projects where we have essentially the same information in three places: in the database schema, in an XML mapping document, and in a class file. This represents two violations of the DRY principle.The solution to this problem is to create a single representation and generate the other two. The first step is deciding who is the “official” holder of this knowledge. For example, if the database is the canonical source, generate the XML mapping and the corresponding class files.In this example, I used Groovy (the scripting language dialect of Java) to solve the mismatch. On the project that spawned this example, the developers had no control over the database schema. Thus, I decided that the database is the canonical representation of the data. I was using the open source iBatis SQL mapping tool (which doesn’t generate SQL; it merely handles mapping classes to SQL results).The first step entailed harvesting the schema information from the database:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- DRY Documentation
- InhaltsvorschauOut-of-date documentation is worse than none because it is actively misleading.Documentation is a classic battleground between management and developers: managers want more and developers want to create less. It is also a battleground in the war against noncanonical representations. Developers should be able to make changes to code aggressively, to improve its structure and allow it to evolve. If you must have documentation for all your code, it must evolve at the same time. But most of the time, they get out of sync because of schedule pressure, lack of motivation (because, let’s face it, writing code is more fun than writing documentation), and other factors.For managers, documentation is about risk mitigation.Out-of-date documentation creates the risk of spreading misinformation (which is ironic, given that part of its purpose is to reduce risk). The best way to prevent documentation from getting out-of-date is to generate as much of it as possible. This section covers a couple of scenarios that make that possible.On one of my projects, we had a problem passing information around. Developers were scattered around the world in Bangalore, New York, and Chicago. We were sharing a single source control repository (in Chicago) and keeping track of important decisions on a wiki (we were using the open source Instiki). At the end of every day, each developer was responsible for updating the wiki to indicate what he or she had done that day. You can guess how well that worked considering the inevitable mad rush to leave the office and catch trains that occurred at the end of the day. We tried nagging the developers, which only irritated everyone.Then we realized that we were actually violating the canonicality principle because we were asking the developers to document stuff they had already documented—in their comments when posting to version control. All the developers were good about writing descriptive comments. We decided to leverage that existing resource, and to that end, we created SVN2Wiki, a little utility designed as a Subversion plug-in. When Subversion performs operations, you can write programs that it will run for you. SVN2Wiki sits and waits for Subversion to call it when someone checks in code. It then harvests the comments added by that developer pair and posts them to the wiki.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Summary
- InhaltsvorschauRepetition is the single most diminishing force in software development.Repetition creeps up on projects in sneaky ways. It sometimes takes ingenuity to figure out ways around it. Glenn Vanderburg, a wise seer of software development, sums it up nicely:
Figure : Relationship diagram produced by SchemaSpyEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 6: Test-Driven Design
- Inhaltsvorschau
Tested code provides greater confidence that the intent matches the result. Test-driven development (TDD) goes even further, insisting that you write tests before you write the code. When comparing software “engineering” to other engineering disciplines (which always requires a good handful of tortured metaphors), major differences pop up. We don’t have centuries of mathematics to rely upon in software. The science of software development hasn’t been around long enough (and we may never get to that level of sophistication). We also can’t take advantage of the economy of scale in traditional engineering. For example, the Golden Gate Bridge contains more than 1,000,000 rivets. You can bet that the engineers who designed that bridge knew the stress characteristics of those rivets and used that number multiplied by 1,000,000 to tell them important things about the stress on the bridge. A piece of software may also have 1,000,000 pieces, but they are all different. We can’t take advantage of the scale and multiplicity that “regular” engineers can. But developers do have an advantage: we can manufacture our components very easily and write code that verifies our components do what we intended them to do. Because it is vanishingly cheap to write software to test software, we can apply our own version of verification through levels of testing: unit testing, functional testing, integration testing, and user acceptance.Testing is the engineering rigor of software development.Rigorously applied TDD has other design benefits as well, so many that I usually refer to TDD as test-driven design. TDD forces you to think about code in a different way. Instead of just writing a pile of code and then writing the tests for it, TDD forces you to think through the testing process before you write the code. TDD creates consumption awareness: when you create a unit test, you are creating the first consumer of the code under development. This forces you to think about how the rest of the world will use this class. Every developer has the experience of writing a class in one big bunch, making assumptions along the way. Then, when it comes time to actually use the class, you realize that some of your assumptions were wrong, and you have to refactor the original code.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Evolving Tests
- InhaltsvorschauLet’s look at an example of the design benefits that TDD affords. To demonstrate them, we need a problem that isn’t so trivial as to be a throwaway, but not so complex that we get caught up in the details. A perfect candidate (pun intended) is a perfect number finder. A perfect number is a number whose factors (minus the number itself) add up to the number. For example, 6 is a perfect number because the factors of 6 (1, 2, 3, and 6) minus the 6 add up to 6. Let’s write some code in Java to find perfect numbers.The following code was written without TDD, just by applying simple logic and some minor mathematical optimizations:
PerfectNumberFinder { isPerfect( number) { List<Integer> factors = ArrayList<Integer>(); factors.add(); factors.add(number); ( i = ; i < Math.sqrt(number) + ; i++) (number 0 == ) { factors.add(i); ( number / i != i ) factors.add(number / i); } sum = ; (Integer i : factors) sum += i; sum - number == number; } }This code has a single static method that returns true or false depending on the perfection of the passed number. The first step gets the factors. I know that 1 and the number itself are factors, so I add them right away. Then, I use a loop to go up to the square root of the number. This is a slight optimization; if you harvest the factors in pairs, you only have to search up to the square root.As it stands, this is just a blob of code. How would it look different if TDD were used? The first test should be almost insanely simple. Here, I just want to get the factors for 1:factors_for_1() { [] expected = [] {}; Classifier c = Classifier(); assertThat(c.getFactors(), is(expected)); }I’m using JUnit 4.4 with the Hamcrest matchers (Hamcrest matchers provide more English-friendly syntax for matchers, such as ). How can this be a useful test? It seems too simple. Really simple tests like this aren’t really about testing, they are about getting the infrastructure set up correctly. I must have the test libraries on the classpath, I need a class named , and I must resolve all the package dependencies. That’s a lot of work! Writing a stupidly simple test allows me to get all the structure established before I have to start thinking about testing the actual hard problems.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Code Coverage
- InhaltsvorschauOne last really important testing topic is code coverage, which refers to the lines and branches executed in your code by tests. Open source and commercial code coverage tools exist for virtually every language.For compiled languages (like Java and C#), code coverage works by first running an instrumentation processor on your compiled byte code. You then run your suite of unit tests against the instrumented code, which measures which lines are executed. The details are written to some intermediate form, from which a report is generated, showing the line and branch coverage of your tests. This is summarized in .
Figure : How code coverage worksThe process is slightly different for dynamic languages, but the end result is the same: a report on how much of your code has been exercised by your tests. The report appears in your IDE, or in an XML or HTML view.This metric is critical because it tells you what has not been tested. Testing is the engineering rigor of software, and untested code is most likely where bugs live. If you are religious about TDD, all your code will be tested automatically, except for unusual fringe cases (for which you should add tests).A lot of developers ask what the acceptable level of code coverage is. I used to be sanguine about this number, setting the acceptable threshold at about 80 percent. Then I noticed an interesting phenomenon: accepting only 80 percent coverage meant that the code that needed testing the most didn’t get tests. Even conscientious developers write some complex code, run the code coverage report, and say “Whew! 82.3 percent. I wasn’t looking forward to figuring out how to test that beast!”I’ve come to the conclusion that anything less than 100 percent code coverage is a dangerous compromise. If you hold yourself to that highest standard, you will never have any code that’s “too complex to test.” You must write simpler code and when you encounter a truly complex scenario, you will be forced to come up with innovative ways to test it. Knowing that you don’t have a “get out of testing free” card will make you more diligent about code hygiene.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 7: Static Analysis
- Inhaltsvorschau
disposal a powerful way to isolate and find categories of bugs that are very difficult to find with code reviews and other traditional means. Static analysis is a programmatic validation mechanism where tools look for known bug patterns in your code.Static analysis tools fall into two broad categories: those that look at compiled artifacts (that is, class files or byte code) and those that analyze source files. I give examples of each in this chapter, and I use Java because it has a very rich set of free static analysis tools. However, this technique is not restricted to Java code; tools exist for all major statically typed languages.Byte code analyzers look for known bug patterns in compiled code. This implies two things. First, some languages have been studied enough to find common bug patterns in the compiled byte code. Second, these tools can’t find arbitrary bugs—they find only bugs for which patterns have been defined. This doesn’t mean that these tools are weak. Some of the bugs they find are extraordinarily difficult to find using other means (that is, by spending many unproductive hours staring at a debugger).One such tool is FindBugs, an open source project from the University of Maryland. FindBugs operates in several different modes: command line, from an Ant task, and from a graphical environment. The FindBugs GUI appears in .
Figure : The FindBugs graphical clientFindBugs finds bugs in several categories:- Correctness
- Indicates probable bug
- Bad practice
- Violation of some recommended and essential coding practice (for example, overriding the method but failing to also override the method)
- Dodgy
- Confusing code, odd usages, anomalies, poorly written code
To illustrate FindBugs, I had to choose a victim, so I downloaded the open source Struts web framework and ran FindBugs against it. It discovered some (probably) false positives in the “bad practice” category under the violation named “Equals method should not assume anything about the type of its argument.” In Java, the recommended best practice when defining an method is to check the lineage of the object passed to the method to make sure that an equals comparison makes sense. This is the offending code in Struts, in the fileEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Byte Code Analysis
- InhaltsvorschauByte code analyzers look for known bug patterns in compiled code. This implies two things. First, some languages have been studied enough to find common bug patterns in the compiled byte code. Second, these tools can’t find arbitrary bugs—they find only bugs for which patterns have been defined. This doesn’t mean that these tools are weak. Some of the bugs they find are extraordinarily difficult to find using other means (that is, by spending many unproductive hours staring at a debugger).One such tool is FindBugs, an open source project from the University of Maryland. FindBugs operates in several different modes: command line, from an Ant task, and from a graphical environment. The FindBugs GUI appears in .
Figure : The FindBugs graphical clientFindBugs finds bugs in several categories:- Correctness
- Indicates probable bug
- Bad practice
- Violation of some recommended and essential coding practice (for example, overriding the method but failing to also override the method)
- Dodgy
- Confusing code, odd usages, anomalies, poorly written code
To illustrate FindBugs, I had to choose a victim, so I downloaded the open source Struts web framework and ran FindBugs against it. It discovered some (probably) false positives in the “bad practice” category under the violation named “Equals method should not assume anything about the type of its argument.” In Java, the recommended best practice when defining an method is to check the lineage of the object passed to the method to make sure that an equals comparison makes sense. This is the offending code in Struts, in the file ApplicationMap.java:entries.add( Map.Entry() { equals(Object obj) { Map.Entry entry = (Map.Entry) obj; ((key == ) ? (entry.getKey() == ) : key.equals(entry.getKey())) && ((value == ) ? (entry.getValue() == ) : value.equals(entry.getValue())); }The reason I flag this as a potential false positive relates to the fact that this is an anonymous inner class definition, and perhaps the author always knows the argument types. It is a bit fishy, though.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Source Analysis
- InhaltsvorschauAs the name implies, source analysis tools look at your source code, searching for bug patterns. The tool I used in the following code is PMD, an open source tool for Java. PMD offers a command-line version, Ant support, and plug-ins for all major development environments. It looks for problems in the following categories:
- Possible bugs
- For example, empty blocks
- Dead code
- Unused local variables, parameters, and private variables
- Suboptimal code
- Wasteful string usage
- Overcomplicated expressions
- “Reuse” via copy and paste
- Duplicate code (supported by an ancillary tool called CPD)
- “Reuse” via copy and paste
PMD falls somewhere between pure style checkers (like CheckStyle, which makes sure that your code adheres to certain style guidelines like indentation) and FindBugs, which looks at byte code. As an example of the kinds of errors for which PMD excels, consider this Java method:insertLineItems(ShoppingCart cart, orderKey) { Iterator it = cart.getItemList().iterator(); (it.hasNext()) { CartItem ci = (CartItem) it.next(); addLineItem(connection, orderKey, ci.getProduct().getId(), ci.getQuantity()); } }PMD will flag this method as improvable by making the first parameter () . By making the parameter , you allow the compiler to do more work for you. Because all objects are passed by value in Java, it is impossible to assign a new object reference to within the method, and attempting to do so indicates an error. PMD offers several clues like this that make existing tools (like the compiler) operate more efficiently.PMD also comes with CPD, the Cut-Paste Detector, which scans your source code for suspiciously duplicated code (like copied and pasted code in Struts). CPD has a Swing-based user interface, showing status and offending code, as shown in . This, of course, relates directly back to the DRY principle discussed in .Most similar analysis tools offer customization APIs, allowing you to create your own rule sets (FindBugs and PMD both have suchEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Generate Metrics with Panopticode
- InhaltsvorschauThe subject of metrics is beyond the scope of this book, but being productive about generating metrics is fair game. For static languages (like Java and C#), I’m a big fan of continuous metrics-gathering to make sure that problem resolution can happen as quickly as possible. Typically, that means that I have a set of metrics tools (including FindBugs and PMD/CPD) that I want to run as part of continuous integration.It’s a hassle having to wire up all that stuff for every project. As with Buildix (see ” in ), I’d like to have all the infrastructure preconfigured. That’s where Panopticode shines.One of my colleagues (Julias Shaw) had the same problem, but instead of just complaining about it (like me), he fixed it. Panopticode is an open source project with lots of common metrics tools preconfigured. The essence of Panopticode consists of an Ant build file with lots of open source projects and their JAR files preconfigured. You supply your source path, your library path (in other words, all the JAR files you need to build your project), and your test directory, then run Panopticode’s build file. It does the rest.Panopticode includes the following preconfigured metrics tools:
- Emma
- Open source code coverage tool (see ” in ). You can switch this to Cobertura (another open source code coverage tool for Java) with a single-line change within Panopticode’s build file.
- CheckStyle
- Open source code style verifier. You can supply your own custom rule sets with a single entry in Panopticode’s build file.
- JDepend
- Open source metrics tool that gathers numeric metrics at the package level.
- JavaNCSS
- Open source cyclomatic complexity tool (see the sidebar ” in ).
- Simian
- A commercial code duplication finder. The version that comes with Panopticode has a 15-day license; you’ll have to pay for it or remove it after that. Future plans are to make this interchangeable with CPD.
- Panopticode Aggregator
- Panopticode reports that show you the results of all these metrics in both textual and graphical form, using tree maps.
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Analysis for Dynamic Languages
- InhaltsvorschauWhile dynamic languages are considered more productive in many development scenarios, they lack the kinds of analysis tools that exist for statically typed languages. Building analysis tools for dynamic languages is more difficult because you don’t have the characteristics of the type system to lean upon.Most of the efforts in the dynamic language world center around cyclomatic complexity (which is universal in virtually every block-based language) and code coverage. For example, in the Ruby world, rcov is a commonly used code coverage tool. In fact, Ruby on Rails comes with rcov preconfigured (you can see an rcov report in ). For cyclomatic complexity, you can use the open source Saikuro.Because of the lack of “traditional” static analysis tools, Ruby developers have gotten clever. A couple of interesting projects have emerged to measure code quality in nontraditional ways. The first is flog. Flog measures ABC: assignments, branches, and calls, with added weight on calls. Flog assigns a weighted value to each line of a method and reports the results like this (this is a run of flog on the example in ” in ):
SqlSplitter .: assignment .: branch .: downcase .: + .: .: create_output_file_from_number .: close .: lit_fixnum .: % .: puts .: lines_o_sql .: each .: make_a_place_for_output_files SqlSplitter .: + .: .: to_s .: assignment .: new .: lit_fixnumThis result indicates that the method is the most complex in the class with a score of 32.4, derived from the values listed below it. The highest complexity in the method revolves around assignments. Thus, to simplify this code you should attack the myriad assignments in the method first.Groovy is a special case because it is a dynamic language that produces Java byte code. This means that you can run the standard Java static analysis tools provided they work against the byte code. However, the results might not be satisfactory. Because Groovy must create lots of proxy and wrapper classes to work some of its magic, you will get lots of references to classes you didn’t actually create. The developers of metrics tools for Java have started taking Groovy into account (for example, the Cobertura code coverage tool is now Groovy-friendly), but it is still early.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 8: Good Citizenship
- Inhaltsvorschau
code, but good citizenship refers to objects that are both aware of their own state and considerate of the state of others around them. Although this may seem simple, developers violate this principle all the time by coding on autopilot. This chapter looks at several different violators of good citizenship and ways to become more responsible citizens.One of the core tenets of object-oriented programming is encapsulation: protecting internal fields from outside meddling. Yet, I’ve seen lots of developers void the intent of encapsulation because they code on autopilot.Here is a scenario. You create a new class, you create a bunch of private member variables for it, you tell the IDE to spit out properties (either get/set methods in Java or properties in C#), and then you engage your brain. Creating public properties for every private field completely destroys the intent of the property mechanism. You might as well make all the member variables public because the properties aren’t helping you at all (and, in fact, they are just making your code more dense for no good reason).For example, let’s say you have a class that has several address fields (such as the typical , , , ). If you create mutating properties for each of these fields, you open the door for someone to make your a bad citizen, meaning that it has an invalid state. It doesn’t make sense in the real world for customers to have incomplete addresses. Generally, they either have a complete address or nothing. Don’t let your code put your customer object (which is supposed to reflect the real-world customer) into a state that doesn’t make sense for your business. The read-only properties are probably OK, but you should create an atomic mutator instead of providing one for each field:Customer { String _adrLine; String _city; String _state; String _zip; addAddress(String adrLine, String city, String state, String zip) { _adrLine = adrLine; _city = city; _state = state; _zip = zip; } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Breaking Encapsulation
- InhaltsvorschauOne of the core tenets of object-oriented programming is encapsulation: protecting internal fields from outside meddling. Yet, I’ve seen lots of developers void the intent of encapsulation because they code on autopilot.Here is a scenario. You create a new class, you create a bunch of private member variables for it, you tell the IDE to spit out properties (either get/set methods in Java or properties in C#), and then you engage your brain. Creating public properties for every private field completely destroys the intent of the property mechanism. You might as well make all the member variables public because the properties aren’t helping you at all (and, in fact, they are just making your code more dense for no good reason).For example, let’s say you have a class that has several address fields (such as the typical , , , ). If you create mutating properties for each of these fields, you open the door for someone to make your a bad citizen, meaning that it has an invalid state. It doesn’t make sense in the real world for customers to have incomplete addresses. Generally, they either have a complete address or nothing. Don’t let your code put your customer object (which is supposed to reflect the real-world customer) into a state that doesn’t make sense for your business. The read-only properties are probably OK, but you should create an atomic mutator instead of providing one for each field:
Customer { String _adrLine; String _city; String _state; String _zip; addAddress(String adrLine, String city, String state, String zip) { _adrLine = adrLine; _city = city; _state = state; _zip = zip; } }Having an atomic mutator means that your object moves from one known legal state to another known legal state in one step. This has a couple of benefits. First, it means that you can skip validation code later to make sure you have a valid address. If you can never create an invalid address, you don’t have to guard against it. Second, it makes your customer abstraction match more closely to the real customer you model. As the real customer changes, your code can change along with it because they are so semantically close.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Constructors
- InhaltsvorschauIn most modern object-oriented languages, we take constructors for granted. We think of them merely as the mechanism used to create new objects. But constructors have a more noble purpose: they tell you what it takes to create a valid object of a certain type. Constructors form a contractual relationship with the consumers of an object, indicating what fields must be populated in order to have a valid object of this type.Unfortunately, the authorities in the language worlds advocate against having meaningful constructors. Most languages virtually insist that all classes have a default constructor (that is, one that takes no parameters). From a citizenship standpoint, this doesn’t make any sense. How many times have you heard one of the business people say, “We need to ship widgets to this customer, but we don’t have any information about it.” You can’t ship things to customers that have no internal state. Objects are the keepers of state, and having an object with no state doesn’t make sense. Virtually every object should start out with at least some minimal initial state. Is it possible in your company to have a customer that doesn’t have a name?Pushing back on default constructors is hard. Many frameworks insist on them and get angry if you don’t supply them. The “must have a default constructor” rule is even codified in Java, in the JavaBeans specification. If a framework or language standard forces the issue, it will win (unless you can replace it with a friendlier one). In that case, treat the default constructor as an anomaly, like the ugly serialization cruft you must sometimes attach to your domain objects.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Static Methods
- InhaltsvorschauStatic methods have one wonderful use: as a black-box, standalone, stateless method. The use of static methods of the
Mathclass in Java illustrates good use. When you call the method, you don’t worry that a subsequent call might return the cube root instead of the square root because some state within the method changed. Static methods work well when they are completely stateless. You get into trouble when you start mixing staticness and state.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Criminal Behavior
- InhaltsvorschauWhat happens when an antisocial criminal moves into the neighborhood? exhibits lots of hostile behavior toward the other citizens in the Java world. It allows engineering purity to override common sense. The constant values that define the months start counting from 0 (which is the standard in the rest of Java), meaning that if you pass the number 2 to set the month, it sets . I understand the consistency of everything being numbered from 0, but it is insane to take a well-known association (month numbers) and override it.
Calendaralso doesn’t maintain its own internal state correctly. What happens when you execute the following code?c = Calendar.getInstance(); c.set(Calendar.MONTH, Calendar.FEBRUARY); c.set(Calendar.DATE, ); System.out.println(c.get(Calendar.MONTH)); System.out.println(c.get(Calendar.DATE));
The output is 2 and 2, which after decryption reveals that it believes that the proper date is . You have told a calendar to set a date of February 31st, and it silently returns . How many times do you tell your friend, “Meet me at the place on February 31st” and your friend says, “You mean March 2nd, right?” knows nothing about its internal state, allowing you to set dates that can never actually exist. Instead of complaining with an exception, it just silently gives you a completely different date. Objects are supposed to be the keepers of state, yet seems to know nothing about its state.Why does act this way? The problem is the ability to set individual fields. You should be forced to perform atomic updates for the calendar, setting the month, date, and year at the same time, which would allow the calendar to validate that it has a real date for the current calendar. However,Calendardoesn’t have that because the method signature would be immensely long. The reason for that? is tracking too much information. It keeps not only date information, but also time information. You’d have to set the date and the time of day, which would be an annoying method signature. When was the last time someone asked you the time and you said, “Hold on—I’ve got to check my calendar”? has far too much responsibility, harming both the object as the keeper of state and the usefulness of the class.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 9: YAGNI
- Inhaltsvorschau
It is the battle cry of agile project development to help prevent speculative development. Speculative development occurs when developers tell themselves, “I’m sure I’m going to need some additional functionality later, so I’ll go ahead and write it now.” This is a slippery slope. The better approach is to build only what you need right now.Speculative development harms software because it adds complexity to code prematurely. As Andrew Hunt and David Thomas stated in The Pragmatic Programmer (Addison-Wesley), software suffers from entropy, which is a mathematical term for the amount of complexity in a system. Entropy hits software hard because complexity makes it hard to make changes, understand the code, and add new features. Usually, in the physical world, things tend toward simplicity unless you add energy to disrupt it. Software is the opposite: because it is so easy to create, it tends toward complexity (in other words, it takes the same physical effort to create both complex and simple software). It can take great effort to pull software back toward simplicity.All developers fall into the gold plating trap. Speculative development is a hard habit to break. It’s really hard when you are in the heat of development to be objective about the clever idea you just had. Will it make the code better, or just add more complexity? Incidentally, that is part of the effectiveness of pair-programming. Having someone else there who offers an objective viewpoint of your brilliant idea is invaluable. Developers have a hard time being objective about their own ideas, especially when those ideas are fresh.Whatever form it takes, though, the health of your software suffers if you indulge in too much speculative development. In its worst form, it leads to frameworks! Frameworks aren’t inherently bad, but they illustrate a symptom of the speculative development disease. Java has this disease worse than any other language. If you add up all the other frameworks in the development world, for all other technologies, Java still has more of them. Java even has meta-frameworks, which are frameworks that make it easy to build other frameworks. This insanity should stop!Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 10: Ancient Philosophers
- Inhaltsvorschau
programmer productivity, yet here it is. It turns out that some of the general discoveries made by ancient (and not so ancient) philosophers have direct bearing on building quality software. Let’s see what a few philosophers have to say about code.Aristotle founded many of the branches of science we know today. In fact, the study of science pretty much traces back to him. He categorized, cataloged, and defined entire fields of thought about the natural world. He also built the foundations of logic and formal thinking.One of the logical principles Aristotle defined was the distinction between essential and accidental properties. Let’s say you have a group of five bachelors, all of whom have brown eyes. Unmarriedness is the essential property of the group. Brown eyes is an accidental property. You can’t make the logical deduction that all bachelors have brown eyes because the eye color is really just coincidental.OK, so what does this have to do with software? Extending this concept a little further leads us to the idea of essential and accidental complexity. Essential complexity is the core of the problem we have to solve, and it consists of the parts of the software that are legitimately difficult problems. Most software problems contain some complexity. Accidental complexity is all the stuff that doesn’t necessarily relate directly to the solution, but that we have to deal with anyway.An example is in order. Let’s say that the essential complexity of a problem is the tracking of customer data by placing it in a database from a web page—a nice, straightforward problem. But, to get it to work within your organization, you must use an old database that has spotty driver support. And, of course, you have to worry about getting permissions to access the database. Some of the database must be cross-checked with similar data that lives in a mainframe somewhere. Now, you have to find out how to connect to the mainframe and extract the data in a form that you can work with. It turns out that you can’t get to the data directly because there is no connector technology from what you are using, so you must get someone to extract the data and put it in a data warehouse so that you can get to it. Does this sound like your job? The essential complexity description can be wrapped up in one sentence. The accidental complexity description could go on forever.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Aristotle’s Essential and Accidental Properties
- InhaltsvorschauAristotle founded many of the branches of science we know today. In fact, the study of science pretty much traces back to him. He categorized, cataloged, and defined entire fields of thought about the natural world. He also built the foundations of logic and formal thinking.One of the logical principles Aristotle defined was the distinction between essential and accidental properties. Let’s say you have a group of five bachelors, all of whom have brown eyes. Unmarriedness is the essential property of the group. Brown eyes is an accidental property. You can’t make the logical deduction that all bachelors have brown eyes because the eye color is really just coincidental.OK, so what does this have to do with software? Extending this concept a little further leads us to the idea of essential and accidental complexity. Essential complexity is the core of the problem we have to solve, and it consists of the parts of the software that are legitimately difficult problems. Most software problems contain some complexity. Accidental complexity is all the stuff that doesn’t necessarily relate directly to the solution, but that we have to deal with anyway.An example is in order. Let’s say that the essential complexity of a problem is the tracking of customer data by placing it in a database from a web page—a nice, straightforward problem. But, to get it to work within your organization, you must use an old database that has spotty driver support. And, of course, you have to worry about getting permissions to access the database. Some of the database must be cross-checked with similar data that lives in a mainframe somewhere. Now, you have to find out how to connect to the mainframe and extract the data in a form that you can work with. It turns out that you can’t get to the data directly because there is no connector technology from what you are using, so you must get someone to extract the data and put it in a data warehouse so that you can get to it. Does this sound like your job? The essential complexity description can be wrapped up in one sentence. The accidental complexity description could go on forever.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Occam’s Razor
- InhaltsvorschauSir William of Occam was a monk who disdained ornate, elaborate explanations. His contribution to philosophy and science is known as Occam’s Razor, which says that given multiple explanations for something, the simplest is the most likely. Obviously, this ties in nicely with our discussion of essential versus accidental complexity. How far into the software stack this goes, however, is surprising.As an industry, we’ve been engaged in an experiment for the last decade or so. This experiment started back in the mid to late ’90s, largely driven by the fact that the demand for software vastly outstripped the supply of people who could write it (this wasn’t a new problem—it’s been going on almost since the idea of business software started). The goal of the experiment: to create tools and environments that would allow average and/or mediocre developers to be productive, regardless of the messy facts already known by people like Fred Brooks (see his book The Mythical Man-Month [Addison-Wesley]). The reasoning was that if we could create languages that keep people out of trouble by restricting the damage they could do, we could produce software without having to pay those annoying software craftsmen ridiculous amounts of money (and you probably wouldn’t be able to find enough of them even then). This thinking gave us tools like dBASE, PowerBuilder, Clipper, and Access—the rise of the 4GLs (4th Generation Languages), which include tool/language combinations like FoxPro and Access.But the problem was that you couldn’t get enough done in those environments. They created what my colleague Terry Dietzler called the “80-10-10 Rule” for Access (which I’ve renamed Dietzler’s Law): you can do 80 percent of what the customer wants in a remarkably short time. The next 10 percent is possible, but takes a lot of effort. The last 10 percent is flat out impossible because you can’t get “underneath” all the tooling and frameworks. And users want 100 percent of what they want, so 4GLs gave way to general-purpose languages (Visual BASIC, Java, Delphi, and eventually C#). Java and C# in particular were designed to make C++ easier and less error prone, so the developers built-in some fairly serious restrictions in the interest of keeping average developers out of trouble. They created their own versions of the “80-10-10 Rule,” only this time the stuff you couldn’t do was much more subtle. Because these languages are general-purpose languages, you can get pretty much anything done…with enough effort. Java kept bumping into stuff that would be nice to do but was way too much work, so frameworks were built. And built. And built. Aspects were added. More frameworks were built.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- The Law of Demeter
- InhaltsvorschauThe Law of Demeter was developed at Northwestern University in the late ’80s. It is best summarized by the phrase, “Only talk to your closest friends.” The idea is that any given object shouldn’t know anything about the internal details of the objects with which it interacts. The name of the law comes from the Roman goddess Demeter, the goddess of agriculture (and therefore of food distribution). Although she was not technically an ancient philosopher, her name makes her sound like one!More formally, the Law of Demeter says that for any object and method, the only methods that should be invoked are the following:
- The methods of the object itself
- Parameters of the method
- Any objects created within the method
In most modern languages, you can make this even shorter using the heuristic “never use more than one dot for any method call.” Here’s an example.Let’s say you have a class with two fields: a name and . also has two fields: title and salary. The Law of Demeter says that it is not acceptable to call through within to get to the field, like this:Job job = Job(, ); Person homer = Person(, job); homer.getJob().setPosition();
So, to make this adhere to the Law of Demeter, you can create a method on to change the job, and then ask the class to perform the work for you:PersonDemo() { Job job = Job(, ); Person homer = Person(, job); homer.changeJobPositionTo(); } changeJobPositionTo(String newPosition) { job.changePositionTo(newPosition); }What benefit has this change created? First, notice that we’re no longer calling the method on the class, we’re using a more descriptive method named . This emphasizes the fact that nothing outside the class has any idea of how position is implemented internally to . Although it looks like a now, internally it might be an enumeration. This information-hiding is the main point: you don’t want dependent objects knowing about implementation details of the internal workings of a class. The Law of Demeter prevents this by forcing you to write methods on classes that specifically hide those details.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Software Lore
- InhaltsvorschauSoftware developers are mostly ignorant of software lore. Because technology moves so quickly, developers have to struggle to keep up with all the changes. What could some ancient (relatively, that is) technology tell me about the kinds of problems I need to solve right now?Certainly reading a book about the syntax of Smalltalk isn’t going to help you if you write only in Java or C#. But all Smalltalk books don’t have just syntax in them: they have the hard-won lessons of the developers who were using a brand-new technology (object-oriented languages) for the first time.Pay attention to the lore of “ancient” technologies.Ancient philosophers created ideas that seem obvious in hindsight but that required great leaps of intellect and courage at the time. Sometimes they suffered greatly because what they said went against the established doctrines. One of the great rebels of history was Galileo, who apparently didn’t believe anything that anyone told him. He always had to try it for himself. The accepted wisdom before his time maintained that a heavier object would fall faster than a lighter one. This was based on Aristotelian thinking, where thinking hard about something logically had more merit than experimenting. Galileo didn’t buy it, so he went to the top of the Leaning Tower of Pisa and dropped rocks. And fired rocks from cannons. And discovered that, nonintuitively, all objects fall at the same rate (if you discount air resistance).What Galileo did was prove that things that seem nonintuitive can in fact be true, which is still a valuable lesson. Some hard-fought knowledge about software development is not intuitive. The idea that you can design the entire software up front, and then just transcribe it seems logical, but it doesn’t work in the real world of constant change. Fortunately, a giant catalog of nonintuitive software lore exists in the Anti Patterns catalog (). This is the ancient lore of software. Rather than gnashing your teeth in frustration when your boss is forcing you to use a library of subquality code, point out to him that he’s falling into the “Standing on the Shoulder of Midgets” anti pattern, and he’ll see that you aren’t the only one who thinks it’s a bad idea.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 11: Question Authority
- InhaltsvorschauEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Angry Monkeys
- InhaltsvorschauThis is a story I first heard from Dave Thomas during a keynote address he delivered called Angry Monkeys and Cargo Cults. I don’t know if it’s true (despite researching it quite a bit), but it doesn’t matter—it illustrates a point beautifully.Back in the 1960s (when scientists were allowed to do all kinds of crazy things), behavioral scientists conducted an experiment where they placed five monkeys in a room with a stepladder and a bunch of bananas hanging from the ceiling. The monkeys quickly figured out that they could climb the ladder and eat the bananas, but every time the monkeys got near the stepladder, the scientists would douse the entire room in ice cold water. You can guess what that generated: angry monkeys. Soon, none of the monkeys would go near the ladder.Then, the scientists replaced one of the monkeys with a new monkey, who had not been subject to the blasts of water. The first thing he did was make a beeline for the ladder, and all the other monkeys beat him up. He didn’t know why they were beating him up, but he quickly learned: don’t go near the ladder. Gradually, the scientists replaced the original monkeys with new monkeys until they had a group of monkeys who had never been doused with cold water, yet they would still attack any monkey that approached the ladder.The point? In software, lots of the practices on projects exist because “that’s the way we’ve always done it.” In other words, because of angry monkeys.Here’s an example that arose during one of my projects. Everyone knows that in Java method names are supposed to start with a lowercase letter, then use CamelCase, where word boundaries are indicated by a capital letter. That’s fine for regular coding, but test names are different. When naming unit tests, you want a nice, long, descriptive name so that you can tell what’s being tested. Unfortunately, . On this particular project, I suggested that we use underscores, between the words in test names, like this:
testUpdateCacheAndVerifyItemExists() { } test_Update_cache_and_verify_item_exists() { }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Fluent Interfaces
- InhaltsvorschauA fluent interface is one of the types of domain-specific language (DSL) styles currently in vogue. In a fluent interface, you try to construct long sequences of code into sentences, rationalizing that complete chunks of thought in spoken languages follow this style. This style of coding is easier to read because, just like with English sentences, you know where one thought ends and the next begins.Here is an example, based on one of my projects. We built an application that dealt with train cars, and each train car had a marketing description. Train cars have lots of rules and regulations associated with them, so getting testing scenarios just right was difficult. We constantly had to ask our business analysts if we had the perfectly nuanced definition of the type of car we needed to test. Here’s a simplified version of what we showed them:
Car car = CarImpl(); MarketingDescription desc = MarketingDescriptionImpl(); desc.setType(); desc.setSubType(); desc.setAttribute(, ); desc.setAttribute(, ); desc.setAttribute(, ); car.setDescription(desc);
While this looks perfectly normal to a Java developer, our business analysts hated it. “Why are you showing me Java code? Just tell me what you mean.” Of course, translation always introduces the chance of error. To mitigate this problem, we created a fluent interface, capturing the same information, but in this form instead:Car car = Car.describedAs() .box() .length() .type(Type.INSULATED) .includes(Equipment.LADDER) .lining(Lining.CORK);Our business analysts liked this much better. We managed to get rid of much of the objectionable redundancy required in the “normal” Java API style. The implementation was very simple. All the set properties returned instead of void, allowing us to create sentences by chaining the method calls together. The implementation of looked like the following:Car { MarketingDescription _desc; Car() { _desc = MarketingDescriptionImpl(); } Car describedAs() { Car(); } Car box() { _desc.setType(); ; } Car length( length) { _desc.setLength(length); ; } Car type(Type type) { _desc.setType(type); ; } Car includes(Equipment equip) { _desc.setAttribute(, equip.toString()); ; } Car lining(Lining lining) { _desc.setLining(lining); ; } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Anti-Objects
- InhaltsvorschauSometimes the authority you should question is your own inclination toward a particular solution to a problem. A great paper appeared at the 2006 OOPSLA conference called Collaborative Diffusion: Programming Anti-Objects. The paper’s authors make the point that, while objects and object hierarchies provide excellent abstraction mechanisms for most problems, those same abstractions make some problems more complex. The idea behind anti-objects is to switch the perceived foreground and background of the problem, and solve the simpler, less obvious problem. What is meant by “foreground” and “background”? An example will clarify. (Warning! If you still enjoy playing PacMan, don’t read the next paragraphs—they will ruin it forever for you! Sometimes knowledge comes with a price.)Consider the PacMan console game. When it came out in the 1970s, it had less computational ability than a cheap cell phone of today. Yet, it had to solve a really difficult math problem: how do you get the ghosts to chase PacMan through the maze? That is to say: what is the shortest distance to a moving target through a maze? That’s a big problem, especially if you have very little memory or processor power to work with. So the developers of PacMan didn’t solve that problem, they used the anti-object approach and built the intelligence into the maze itself.The maze in PacMan acts like an automata (like in Conway’s Game of Life). Each cell has simple rules associated with it, and the cells executed one at a time, starting at the upper left and proceeding to the lower right. Each cell remembers a value of “PacMan smell.” When PacMan sits on a cell, it has maximum PacMan smell. If he had just vacated the cell, it has maximum PacMan smell –1. The smell degrades for a few more turns, then disappears. The ghosts can then be dumb: they just sniff for PacMan smell, and any time they encounter it, they go to the cell that has a stronger smell.The “obvious” solution to the problem builds intelligence into the ghosts. Yet, the much simpler solution builds the intelligence into the maze. That is the anti-object approach: flip the computational foreground and background. Don’t fall into the trap of thinking that “traditional” modeling is always the correct solution. Perhaps a particular problem is more easily solved in another language entirely. (See for the rationale behind this anti-object approach.)Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 12: Meta-Programming
- Inhaltsvorschau
but the practical definition is much broader. In general, any solution that manipulates code outside “normal” use is considered meta-programming. Meta-programming approaches tend to be more complex than traditional solutions (for example, libraries and frameworks), but because you are manipulating code at a more fundamental level, it makes hard things easier and impossible things merely improbable.All major modern languages have some level of meta-programming support. Learning the meta-programming facilities of your primary language will save you major effort and open new avenues to finding solutions.I talk about several examples of meta-programming in this chapter, giving you a flavor in Java, Groovy, and Ruby. Each language’s capabilities are different; the following examples simply show the kinds of problems you can solve using meta-programming.Java’s reflection is robust but limited. You can certainly call methods with representations of their names, but the security manager won’t allow you to define new methods or overwrite existing methods at runtime. You can do some of that with Aspects, but that’s arguably not really Java because it has its own syntax, compiler, etc.An example of when you might want to use Java’s reflection is for testing private methods. When you are writing code using test-driven development (TDD), you still want to use the protection mechanisms built-in to the language. Using reflection to call the private method is pretty straightforward, but requires lots of syntax.Let’s say that you needed to call a method named (which returns whether a number is a factor of another number) on a class named . To call the private method, you can create a helper method in your test class that looks like this:isFactor( factor, number) { Method m; { m = Classifier..getDeclaredMethod(, .); m.setAccessible(); (Boolean) m.invoke( Classifier(number), factor); } (Throwable t) { fail(); } ; }The unit test then becomes trivial:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Java and Reflection
- InhaltsvorschauJava’s reflection is robust but limited. You can certainly call methods with representations of their names, but the security manager won’t allow you to define new methods or overwrite existing methods at runtime. You can do some of that with Aspects, but that’s arguably not really Java because it has its own syntax, compiler, etc.An example of when you might want to use Java’s reflection is for testing private methods. When you are writing code using test-driven development (TDD), you still want to use the protection mechanisms built-in to the language. Using reflection to call the private method is pretty straightforward, but requires lots of syntax.Let’s say that you needed to call a method named (which returns whether a number is a factor of another number) on a class named . To call the private method, you can create a helper method in your test class that looks like this:
isFactor( factor, number) { Method m; { m = Classifier..getDeclaredMethod(, .); m.setAccessible(); (Boolean) m.invoke( Classifier(number), factor); } (Throwable t) { fail(); } ; }The unit test then becomes trivial:is_factor() { assertTrue(isFactor(, )); assertTrue(isFactor(, )); assertFalse(isFactor(, )); }In the previous example, you just ate whatever exceptions occurred because of the reflection code (Java is very worried about reflection and requires that you catch a lot of different types of exceptions). Most of the time you can get by with failing the test because something’s just not working. In other situations, though, you have to be a little more careful. Here’s an example of a test class helper that has to tiptoe around the exceptions that legitimately bubble out of the method called via reflection:calculateFactors(Classifier c) { Method m; { m = Classifier..getDeclaredMethod(); m.setAccessible(); m.invoke(c); } (InvocationTargetException t) { (t.getTargetException() InvalidNumberException) (InvalidNumberException) t.getTargetException(); fail(); } (Throwable e) { fail(); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Testing Java with Groovy
- InhaltsvorschauGroovy is the dynamic language syntax for Java. As such, it can interact pretty seamlessly with Java code (including compiled byte code), allowing you much more flexible syntax. Groovy makes it possible for you to do a bunch of stuff on the Java platform that is difficult or impossible using Java.You can invoke the standard reflection mechanism in Java using the Groovy syntax as shown in this listing, which replicates the test for shown earlier:
is_factor_via_reflection() { def m = Classifier..getDeclaredMethod(, .) m.accessible = assertTrue m.invoke( Classifier(), ) assertTrue m.invoke( Classifier(), ) assertFalse m.invoke( Classifier(), ) }As you can see, the reflection code is so concise that I didn’t feel compelled to put it in its own method. Groovy swallows the noisy checked exceptions for you, making it easier (or at least less formal) to call the reflection methods. Groovy “understands” Java property syntax so that the call is equivalent to the call Java . Groovy also has looser rules about parentheses.The code shown here tests the exact same code as the unit test shown above—it literally uses the same JAR file to access the code. Groovy makes it easier to write unit tests for Java code, which is a good excuse for “sneaking” it into conservative organizations (after all, testing code is part of the infrastructure, not deployed to production, so who cares what open source libraries you use, right?). In fact, I’m a big advocate of not calling “Groovy” by its name when nondevelopers are around. I prefer calling it the Enterprise Business Execution Language (using the acronym ebXl—managers think that acronyms with capital Xs in them are sexy).Actually, the earlier test isn’t the entire story. It turns out that in its current incarnation, Groovy ignores theprivatekeyword entirely, even when something is declared in Java code. Thus, the earlier test could be written like this:is_factor() { assertTrue Classifier().isFactor() assertTrue Classifier().isFactor() assertFalse Classifier().isFactor() }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Writing Fluent Interfaces
- InhaltsvorschauHere is a short example of using Ruby’s meta-programming support to write a fluent interface that goes beyond Java’s capabilities. Obviously, an entire book could be written about this subject—this will just give you a taste. This code works on any version of Ruby, including the Java platform port, JRuby.You want to write a fluent interface that represents recipes. It should allow developers to write the ingredients for the recipes in what looks like a data format, but underneath it should build a nutrition profile. The ability to encapsulate the actual behavior of code is one of the advantages of fluent interfaces. Here is the target syntax:
recipe = .new recipe.add .grams.of recipe.add .lb.of
To make this code work, you must first add new methods to the built-in class in Ruby (which encompasses both integer and floating-point numbers):alias_method , * alias_method , alias_method , alias_method ,Open classes in Ruby allow you to add new methods to existing classes. The syntax is quite simple: when you create a class definition, it opens the existing class if that class already appears on the classpath. The built-in type is obviously already on the classpath, so this code adds new methods to it. I’m keeping track of recipe weights in grams, so the method merely returns the value of the number, and returns the number of grams in a pound.The changes to the class make the first part of my recipe fluent interface work. What about the second part?ingredient ingredient.kind_of? ingredient = .new(ingredient) ingredient.quantity = ingredientReopening again allows you to add the method to numbers. This method works with either s or existing ingredients (the code checks to see what has been passed), sets the quantity of the object to the value of the number, and then returns the instance (the return isn’t strictly necessary; the last line of any Ruby method is the return of the method, but the explicit call makes the code a little more readable).Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Whither Meta-Programming?
- InhaltsvorschauSeeing all this meta-programming code may make you queasy because it violates the cardinal rule of not writing self-modifying code. But this is precisely a place where you should question authority (see ). Yes, this can be dangerous, when used incorrectly. But that’s true of any powerful feature. You can do dangerous things using Aspects in Java as well, it’s just more difficult. But it’s a bad argument to say that powerful language features should be so difficult that only masters can attain them. Much of the philosophy of Java was to remove power from the hands of developers by making the class . But an interesting thing happened: building restrictions into the language didn’t make the poor developers any better, and it placed a governor on the best developers, making them jump through ridiculous hoops to get things done. The classic example playing out right now in Groovy concerns how to handle s. The is the class in Groovy that offers lots more features than the Java . Because Groovy interacts so intimately with Java, it would be helpful to be able to use and interchangeably, certainly in Groovy code that passes strings to Java code. But you can’t. Because the class is declared as , you can’t even subclass s from s so that Java libraries can understand them. The existence of is an admission of the language designers that they don’t trust the people using the language.Languages with strong meta-programming support take the opposite approach: they allow developers extraordinary power, and let them decide when to use it.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 13: Composed Method and SLAP
- Inhaltsvorschau
While the concept comes from Kent Beck’s Smalltalk Best Practice Patterns (Prentice Hall), my friend Glenn Vanderburg is the one who captured its essence with this great acronym.Before I talk about SLAP, though, I must talk about the composed method pattern discussed in Beck’s book. Composed method mandates that all public methods read like an outline of the steps to perform. The actual steps are implemented as private methods. Composed method is a way of factoring your code to keep it cohesive and make it easier to spot candidates for code reuse. The best way to understand composed method is to see it in action.Composed method encourages factoring (or refactoring) code into small, cohesive, readable chunks. For projects on which I’m the tech lead, our rule of thumb is to allow no method to exceed 15 lines of code in Java or C#. For dynamic languages such as Groovy or Ruby, the rule is five lines of code.What benefit does this provide? Consider the following noncomposed method code, from a small e-commerce site:populate() Exception { Connection c = ; { Class.forName(DRIVER_CLASS); c = DriverManager.getConnection(DB_URL, USER, PASSWORD); Statement stmt = c.createStatement(); ResultSet rs = stmt.executeQuery(SQL_SELECT_PARTS); (rs.next()) { Part p = Part(); p.setName(rs.getString()); p.setBrand(rs.getString()); p.setRetailPrice(rs.getDouble()); partList.add(p); } } { c.close(); } }This method is part of a larger class that uses low-level Java Database Connectivity (JDBC) to harvest information from a database. Nothing in this code really jumps out as a candidate for reuse. But it violates the 15-line guideline, and it seems like it’s doing a lot of stuff, so you should refactor it.The first step entails refactoring out the parts that seem like the steps you’re performing. If you wrote out in English what this method does, you’d have a good idea what the new method names should be. After a refactoring pass, you end up with this:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Composed Method in Action
- InhaltsvorschauComposed method encourages factoring (or refactoring) code into small, cohesive, readable chunks. For projects on which I’m the tech lead, our rule of thumb is to allow no method to exceed 15 lines of code in Java or C#. For dynamic languages such as Groovy or Ruby, the rule is five lines of code.What benefit does this provide? Consider the following noncomposed method code, from a small e-commerce site:
populate() Exception { Connection c = ; { Class.forName(DRIVER_CLASS); c = DriverManager.getConnection(DB_URL, USER, PASSWORD); Statement stmt = c.createStatement(); ResultSet rs = stmt.executeQuery(SQL_SELECT_PARTS); (rs.next()) { Part p = Part(); p.setName(rs.getString()); p.setBrand(rs.getString()); p.setRetailPrice(rs.getDouble()); partList.add(p); } } { c.close(); } }This method is part of a larger class that uses low-level Java Database Connectivity (JDBC) to harvest information from a database. Nothing in this code really jumps out as a candidate for reuse. But it violates the 15-line guideline, and it seems like it’s doing a lot of stuff, so you should refactor it.The first step entails refactoring out the parts that seem like the steps you’re performing. If you wrote out in English what this method does, you’d have a good idea what the new method names should be. After a refactoring pass, you end up with this:PartDb { String DRIVER_CLASS = ; String DB_URL = ; DEFAULT_INITIAL_LIST_SIZE = ; String SQL_SELECT_PARTS = ; Part[] TEMPLATE = Part[]; ArrayList partList; PartDb() { partList = ArrayList(DEFAULT_INITIAL_LIST_SIZE); } Part[] getParts() { (Part[]) partList.toArray(TEMPLATE); } populate() Exception { Connection c = ; { c = getDatabaseConnection(); ResultSet rs = createResultSet(c); (rs.next()) addPartToListFromResultSet(rs); } { c.close(); } } ResultSet createResultSet(Connection c) SQLException { c.createStatement(). executeQuery(SQL_SELECT_PARTS); } Connection getDatabaseConnection() ClassNotFoundException, SQLException { Connection c; Class.forName(DRIVER_CLASS); c = DriverManager.getConnection(DB_URL, , ); c; } addPartToListFromResultSet(ResultSet rs) SQLException { Part p = Part(); p.setName(rs.getString()); p.setBrand(rs.getString()); p.setRetailPrice(rs.getDouble()); partList.add(p); } }Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - SLAP
- InhaltsvorschauSLAP insists that all your code within a method live at the same level of abstraction. In other words, you shouldn’t have a method in which part of it deals with low-level database connectivity, another part with high-level business code, and another with web service plumbing. Of course, such a method would also violate Beck’s composed method rule. But even if you have a cohesive method, you should make sure that all the lines of code share the same abstraction level.Here’s an example. Consider this method, taken from a sample JEE e-commerce site (slightly more complex than the one in the previous examples). This particular method takes a shopping cart and adds an order (which in turn adds line items). For simplicity’s sake, it also uses low-level JDBC, but that’s peripheral to the SLAP discussion.
addOrder(ShoppingCart cart, String userName, Order order) SQLException { Connection c = ; PreparedStatement ps = ; Statement s = ; ResultSet rs = ; transactionState = ; { c = dbPool.getConnection(); s = c.createStatement(); transactionState = c.getAutoCommit(); userKey = getUserKey(userName, c, ps, rs); c.setAutoCommit(); addSingleOrder(order, c, ps, userKey); orderKey = getOrderKey(s, rs); addLineItems(cart, c, orderKey); c.commit(); order.setOrderKeyFrom(orderKey); } (SQLException sqlx) { s = c.createStatement(); c.rollback(); sqlx; } { { c.setAutoCommit(transactionState); dbPool.release(c); (s != ) s.close(); (ps != ) ps.close(); (rs != ) rs.close(); } (SQLException ignored) { } } }The method consists of a bunch of steps to set up database infrastructure, then moves to higher-level business domain methods like . It is hard to read code like this because it leaps between abstraction levels almost randomly, based on what steps need to occur next.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 14: Polyglot Programming
- Inhaltsvorschau
Just like spoken languages, computer languages continuously evolve (although, fortunately, we don’t have teenagers adding slang to our computer languages at the rate at which they are adding it to the English language). Languages evolve to meet the changing environments in which they find themselves. For example, Java recently added generics and annotations in its continuing arms race with .NET. At some point, though, this becomes counterproductive. If you look at a couple of languages from the past (Algol 68 or Ada), you will see that there is a limit to how far you can push a language before it becomes cumbersome and starts to collapse under its own weight. Have we reached that point with Java? And, if we have, what does the future hold?This chapter introduces the idea of polyglot programming as the future of the Java and .NET platform and all those who love it and hold it dear. But, before we get into that, we need to see how we got to where we are now. What’s wrong with Java, and how is this new idea going to fix it?Java is an entrenched mainstay of corporate and other development today. It has been amazing to watch for those of us who lived in a time when Java was either an Indonesian island or a beverage. But popularity doesn’t equate with perfection: Java has its share of warts, mostly due to legacy baggage (which is interesting, as Java was created as a new language and didn’t have any backward compatibly requirements). Let’s look at how Java got here, and just where “here” is.A mythical creature of the dim past (let’s call him James) needed to build a new language for toasters and cable TV boxes. He didn’t want to use the languages he already knew and loved (C and C++) because, even for someone who loved them, they were ill-suited to this type of job. Sure, you can reboot a computer a couple of times a day because of memory management problems, but it’s extra-annoying to do that for a cable box.One day, James decided to create a new language that would solve some of the problems of the beloved but flawed existing languages. He created Oak, which then became Java. (OK, I skipped a few parts of this legend.) And it was good. Java fixed many of the ills of C and C++, and hitched a ride on the burgeoning Internet. Bruce Tate called Java’s rise in popularity a “perfect storm”: all the right conditions fell into place to support Java’s meteoric rise in popularity and use.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - How Did We Get Here? And Where Exactly Is Here?
- InhaltsvorschauJava is an entrenched mainstay of corporate and other development today. It has been amazing to watch for those of us who lived in a time when Java was either an Indonesian island or a beverage. But popularity doesn’t equate with perfection: Java has its share of warts, mostly due to legacy baggage (which is interesting, as Java was created as a new language and didn’t have any backward compatibly requirements). Let’s look at how Java got here, and just where “here” is.A mythical creature of the dim past (let’s call him James) needed to build a new language for toasters and cable TV boxes. He didn’t want to use the languages he already knew and loved (C and C++) because, even for someone who loved them, they were ill-suited to this type of job. Sure, you can reboot a computer a couple of times a day because of memory management problems, but it’s extra-annoying to do that for a cable box.One day, James decided to create a new language that would solve some of the problems of the beloved but flawed existing languages. He created Oak, which then became Java. (OK, I skipped a few parts of this legend.) And it was good. Java fixed many of the ills of C and C++, and hitched a ride on the burgeoning Internet. Bruce Tate called Java’s rise in popularity a “perfect storm”: all the right conditions fell into place to support Java’s meteoric rise in popularity and use.When Java first came out, the Internet and browsers were everyone’s darlings. Java was a bit slow on that era’s hardware and operating systems, but it could perform a trick that no one else could: run in a browser as an applet. While that seems odd now, applets were the thing that put Java on everyone’s radar. Of course, ironically enough, things have come full circle: we’re writing rich client applications that run in a browser, but now mostly with JavaScript, which is seeing its own resurgence in popularity.By the time everyone figured out that running huge corporate applications in a browser wasn’t a good idea, server-side Java had made its debut, adding words likeEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Where Are We Going? And How Do We Get There?
- InhaltsvorschauFortunately, the creators of Java actually built two things: the Java language and the Java platform. And the latter is our escape hatch away from the baggage of the past. Increasingly, Java is being used more as a platform than a language. This trend is going to continue and pick up steam over the next couple of years. In fact, we will eventually all be engaged in what I call polyglot programming.When we build web applications today, we primarily develop in three languages (four, if you count XML): Java (or some other base, general-purpose language), SQL, and JavaScript (in the form of Ajax libraries). Yet most developers will say they are Java (or .NET or Ruby) programmers, leaving out the special-purpose languages that have infiltrated their “normal” general-purpose language.Polyglot programming refers to building applications using one or more special-purpose languages in addition to a general-purpose language. We already do this, but it is so natural that we don’t even think about it. For example, SQL has so ingrained itself into application development that it is taken for granted that virtually every application will use it.SQL is a strange beast, though, compared to the general-purpose, imperative languages we generally use. SQL is all about set theory and manipulating data. As such, it doesn’t look or feel like a “regular” language. Most developers are fine with this dichotomy. They happily use SQL (or allow Hibernate to generate it for them), debug bizarre SQL problems, and even help databases optimize SQL based on profiling. This is a mundanely natural part of everyday software development.Why do we use special-purpose languages? Well, to serve special purposes. SQL clearly belongs in that category. JavaScript does as well, especially the way we use it today. Even though these different languages target different platforms (Java on the virtual machine, SQL on the database server, JavaScript in the browser), they all coalesce into an “application.”Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Ola’s Pyramid
- InhaltsvorschauThe polyglot style of development will continue to lead us in the direction of domain-specific languages (DSLs) as well. In the near future, our language landscape will look very different: specialized languages will be used as the building blocks to create very specific DSLs that are in turn very close to the problem domains we are trying to solve. The era of single-use general-purpose languages is drawing to a close; we’re entering a new realm of specialization. Maybe it’s time to dust off that college Haskell textbook!My colleague, Ola Bini, has added some nuance to this idea of polyglot programming, defining the new application stack. His view of the modern development world looks like . This diagram suggests that we’ll use a language (perhaps a statically typed language) for the stability layer, a more productive language (probably dynamic in nature, like JRuby, Groovy, or Jython) for day-to-day coding, and domain-specific languages (as discussed in ” in ) to align our code more closely to the needs of business analysts and end users. I think Ola has nailed the way that the disparate ideas of polyglot programming, domain-specific languages, and dynamic languages all fit together.
Figure : Ola’s PyramidAll doctors at one time were general practitioners, but as their field advanced, specialization became inevitable. Software complexity is quickly driving us toward specialization, both because of the types of applications we must write and the underlying platforms. To address this brave new world, we should embrace polyglot programming to provide more specialized tools for the platforms, and domain-specific languages to address increasingly difficult problem domains. Software development in the next five years will look very different than it does today!Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Chapter 15: Find the Perfect Tools
- Inhaltsvorschau
of different tools: batch files, bash scripts (both one-liners and full-blown scripts), Windows PowerShell, Ruby, Groovy, sed, awk, and a whole menagerie of big-eyed O’Reilly animals. Now the critical time has come. You’ve identified a problem that is causing grief and you want to automate it away: which tool do you use? The first tool you’ll probably use is a lowly text editor. So, I’ll start by talking about this perhaps most important tool in your arsenal.Developers still spend a lot of time with plain text. No matter how many wizards and other sorcerers we develop, most coding is still in plain text. Most of the information you keep should also reside in plain text because you never know if the tool you are using will be around in five years. It’s a good bet that you’ll be able to read plain ASCII (and probably Unicode) for the next century or so. (As The Pragmatic Programmer [Addison-Wesley] admonishes: “Keep knowledge in plain text.”)Because so much of what we do revolves around text, it makes sense to find your perfect text editor. This isn’t about IDEs; company policy and the language you use generally dictate that. IDEs are great at producing source code. But they are lacking in some of the best tools for plain old text.I’ll admit that I once had an editor fetish. I had a half dozen editors installed on my machine because I liked different features of each one. One day, I decided to create a list of all the things I thought a perfect editor should have, learn every nuance, and get out of the editor-of-the-day business forever.Find your perfect editor and learn it inside and out.Here is my list. These are the things I think the perfect editor should have, along with the editors I’ve found that embody this ideal. Your list will probably differ.A macro recorder
Back in the olden days (a couple of decades ago), macros were one of the most important parts of a developer’s arsenal. I still never go a full week without recording at least one. The advent of live templates inEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Quest for the Perfect Editor
- InhaltsvorschauDevelopers still spend a lot of time with plain text. No matter how many wizards and other sorcerers we develop, most coding is still in plain text. Most of the information you keep should also reside in plain text because you never know if the tool you are using will be around in five years. It’s a good bet that you’ll be able to read plain ASCII (and probably Unicode) for the next century or so. (As The Pragmatic Programmer [Addison-Wesley] admonishes: “Keep knowledge in plain text.”)Because so much of what we do revolves around text, it makes sense to find your perfect text editor. This isn’t about IDEs; company policy and the language you use generally dictate that. IDEs are great at producing source code. But they are lacking in some of the best tools for plain old text.I’ll admit that I once had an editor fetish. I had a half dozen editors installed on my machine because I liked different features of each one. One day, I decided to create a list of all the things I thought a perfect editor should have, learn every nuance, and get out of the editor-of-the-day business forever.Find your perfect editor and learn it inside and out.Here is my list. These are the things I think the perfect editor should have, along with the editors I’ve found that embody this ideal. Your list will probably differ.
A macro recorder
Back in the olden days (a couple of decades ago), macros were one of the most important parts of a developer’s arsenal. I still never go a full week without recording at least one. The advent of live templates in IDEs replaced a lot of the bread-and-butter tasks of macros. In fact, the most popular development environment for Java (the open source Eclipse) still doesn’t have a macro recorder. If you had told a 1980s-era Unix programmer that a development tool existed that wouldn’t let you record macros, he would have been horrified. (To be fair, the next version of Eclipse is supposed to grow this support.)Record macros for all symmetrical text manipulation.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Candidates
- InhaltsvorschauHere are some of the editors that meet most of the criteria I laid out earlier. This isn’t meant to be an exhaustive list, but it’s a good place to start.
- VI
- Of course, this one had to make the list. Many of the advanced features mentioned above started with this editor and the ones it was based upon. VI is still around and going strong. The most popular cross-platform version is called VIM (for “VI Improved”) and exists for all platforms. The only place where it falls a bit short is the readable macro syntax requirement. VI is incredibly hard to learn; it has a virtual cliff-face learning curve. But, once you’ve mastered it, you are the most effective manipulator of text possible. Watching experienced VI editors, people say that the cursor just follows their eyes. Of course, there is low-level warfare between the VI and Emacs crowds, but they are really different things: VI strives to be the ultimate text manipulation tool, and Emacs strives to be an IDE for whatever language you type in. VI wags say that “Emacs is a great operating system with rudimentary text editing support.”
- Emacs
- This is the other old-school editor that has a devoted (no, fanatical) following. It supports all the features listed above (if you count elisp, Emacs’ macro language, as “readable”). It exists as Emacs, XEmacs (a graphical skin on Emacs for operating systems like Windows), and AquaEmacs (specifically for Mac OS X, it uses the native Mac OS X commands in addition to the traditional Emacs commands). Emacs is sometimes finger-twisting to get stuff done (some wags say that Emacs stands for “Escape Meta Alt Control Shift”) but it packs a huge amount of power. It has “modes” for different languages, allowing sophisticated syntax highlighting, special-purpose tools, and a host of other behaviors. Emacs is in fact the prototype for modern
Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Choosing the Right Tool for the Job
- InhaltsvorschauIn his book The Paradox of Choice (Harper Perennial), Barry Schwartz cites a study showing that users are paralyzed by too many choices. Rather than being happy that they have lots of choices, too many choices make them uncomfortable. For example, there was a store that sold jam, and to allow customers to sample their wares, they put out a table with three jars of jam. Sales of jam skyrocketed because customers liked being able to taste the jam before they bought it. Using logic, the proprietors decided to put out 20 jars of jam for tasting. And the sales of jam plummeted. Having three jars was good because people liked to be able to sample. But having 20 jars became overwhelming. People still sampled jam, but there were so many choices, it paralyzed their decision-making process, leading to fewer jam sales.We software developers have the same issue when it comes time to solve a problem: there are so many ways to attack it, we are sometimes prevented from even starting. For example, in the example created to bust SQL files into smaller chunks cited in ” (), the developer with whom I was pair-programming initially thought about using sed, awk, C#, and even Perl to try to solve the problem, but quickly realized that it would take too long. Given the number of choices we have, how do you pick?Increasingly, I’ve been leaning on what I call “real” scripting languages for more and more of my automation chores. By that, I mean a general-purpose language that supports scripting but has the kind of heavy-duty support of a general-purpose language. You can never tell when a little “jig” or “shim” will grow up to be a real part of your project. Useful little utilities that you create one day to handle some little chore have a way of sticking around, gradually growing new functionality as you need to do more stuff. At some point, it graduates into a real part of your project, and you’ll want to start applying the same kinds of rigor to it as your “real” code (like version control, unit testing, refactoring, etc.). Examples of “real” scripting languages include Ruby, Python, Groovy, Perl, etc.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Un-Choosing the Wrong Tools
- InhaltsvorschauAs important (or perhaps even more so) than choosing the right tools is rejecting bad tools. In fact, an anti-pattern exists that describes this: boat anchor. A boat anchor is a tool that you are forced to use even though it is ridiculously ill-suited for the job at hand. Frequently, this boat anchor costs a vast amount of money, increasing the political pressure to use it for every situation. For a tortured but depressingly accurate metaphor, imagine that a carpenter was forced to use a sledge hammer (undoubtably powerful) for driving nails. The unsuitability in this case is obvious, but we make similar and even worse decisions in software.I was recently involved in the inception of a project at a large enterprise. We were introducing many Agile tenets to the developers, who viewed them as fresh air. One of the decisions involved version control. The Infrastructure Control representative came to talk to us and provided two choices: Rational ClearCase or Serena Version Manager. In case you aren’t familiar with these two version control packages, both are quite expensive and have vast functionality (with a corresponding footprint). Our choice? None of the above. We suggested Subversion, the lightweight open source version control package. We described it to the Infrastructure Control people, and they agreed that it sounded perfect for their needs. Then the big question “How much is the licensing fee per user?” They were stunned when we told them that it was free. Then, reflectively, they said, “You know, we installed ClearCase for another group of developers about six months ago, and they don’t seem to like it very much.”Large companies get into an acquisition mode where they try to find one-size-fits-all tools for development. It makes sense for a large company to standardize on infrastructure. But at some point, the standard infrastructure becomes more of an impediment than a benefit. This is especially true for overly complex tools. In fact, I’ve coined a term for it:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
- Chapter 16: Conclusion: Carrying on the Conversation
- Inhaltsvorschau
compare it to other activities and professions. It combines engineering and craft in highly coupled ways, requiring good developers to exhibit a wide variety of skills: analytical thinking; extreme attention to detail on multiple levels and aesthetics; an awareness of macro and micro level concerns simultaneously; and a keen, fine-grained understanding of the subject matter we’re writing software to assist. Ironically, developers have to understand business processes at a much lower level than the actual practitioners of the business. Business people can use their experience to make on-the-fly decisions when novel situations pop up; we must encode everything in algorithms and explicit behavior.The original vision for The Productive Programmer was to provide a long list of recipes for productivity. It morphed into a two-part book, where the first part deals with the mechanics of productivity and the second concentrates on the practices of productivity as a developer. In its final form, this book still contains a fair number of recipes. But following recipes leaves you with tasty dishes, but no capability to create your own recipes. Identifying the mechanical productivity principles (acceleration, focus, automation, and canonicality) provided a nomenclature for recognizing new techniques that I never thought about. In essence, I wanted to create a cookbook that also shows you how to create your own recipes., Practice, was designed to make you think about building software in ways that perhaps you hadn’t before. Developers sometimes fall into ruts, necessitating a third party to come along and point out new ways of thinking. Hopefully, did that.In effect, the meta-goal of this book is to create a dialogue, not a monologue, about productivity at both the mechanics level and the practices level. I want to raise the awareness of how we as developers can become more productive. At the same time, I want other, much smarter people to carry on this conversation. Collectively, we can come up with a lot of amazingly cool stuff.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Appendix : Building Blocks
- Inhaltsvorschau
Command lines are wonderful things. If you know the magical commands, the command line is usually the quickest route from intention to execution. Once upon a time, developers had no choice—they had to learn all the incantations by heart, and computer magazines filled up with all the interesting nuances of how DOS worked (and frequently, didn’t work). When Windows conquered the users’ desktops, the developers followed, and only us “seasoned” developers knew how to use the real dark magic under the skin.Even though IDEs make novice developers more productive, the most productive developers still rely, even thrive, on this command-line Judo. Automating tasks through scripting, linking the inputs and outputs of existing tools, and performing small tasks on local and remote files is still best done at the blinking cursor. But first, you have to make sure you have the right blinking cursor. If you are on Unix or Mac OS X, you can skip the next section. If you are on Windows, you desperately need to read it.Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - Cygwin
- InhaltsvorschauAs a Windows user, have you ever gotten jealous of the sheer amount of stuff that comes with a Linux distribution? Compilers for a dozen languages, debuggers, text editors, drawing tools, web servers, databases, publishing tools…the list goes on and on. You can have all that on top of Windows, too—thanks to Cygwin.Cygwin is a combination of several things:
- A Linux API emulation layer, allowing you to compile and run all those cool Linux programs.
- A collection of all those really great Unix-like tools.
- An installer/program manager for keeping the whole thing up-to-date.
First, download the Cygwin installer from . This is more than just an installer: it is a complete package management system. Even after you install Cygwin, you should keep this around; you will need it to install, update, and remove packages in the future. The installer you download is very small (about 300k), but that is just the tip of the iceberg—it will download a lot of stuff, potentially hundreds of megabytes depending on what options you decide to install (you won’t need to install that much to run the examples in the book). For the bandwidth-impaired, you can purchase install CDs for a minimal fee.
Figure : The Cygwin installer is actually a package managerThe Cygwin installer is a bit odd to most Windows users’ eyes. The package selection screen shows you a bunch of stuff to install, and each one can be selected for installation, upgrade, or removal independently. At first, this is a little confusing; it helps if you keep in mind that this is a “package manager,” not just an “installer.”Next, you have to make choices involving DOS versus Unix line endings. The short answer for the purpose of this book is to select Unix, but the reasons are more arcane. There are invisible characters at the end of every line, and the order of these invisible characters is different between DOS and Unix. Since you are installing Unix tools on your machine, you are now going to haveEnde der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar. - The Command Line
- InhaltsvorschauWhy is the command line so useful, especially in the Unix world? Why do all the Unix geeks get all misty eyed when they talk about it, and drool into their Birkenstocks? It goes back to the philosophy that the Unix creators built into the command line. They wanted a powerful set of tools that you could mix and match to form powerful composites. To that end, they built everything around a simple concept: streams of plain text. Virtually all the Unix command-line tools produce and consume plain text. Even text files can be converted into a stream of plain text (via the cat command) and put back into a file with the redirection command, the >.For a simple example, you can take regular text with the echo command and pipe it through something that converts it from lowercase to uppercase (using the tr, or translate, command). This understands character classes (like :lower: and :upper:) like this (note that the
$isn’t part of the command, the command-line prompt, left there so you can tell input from output):$ PRODUCTIVELY LAZY
The concept worth noticing here is the pipe command (the | character). It takes the output of the echo command and passes it into the tr command, which dutifully translates lowercase characters into uppercase ones. All Unix commands are wired this way, where the output of one becomes the input of the next. You can also create a file with that content by redirecting the output of this command into a file:$ pl.txt $ pl.txt PRODUCTIVELY LAZY
And, of course, you can take text from one file, do something to it, and put it into another file:$ pl.txt plz.txt productively laZy
Here is a more practical example. Suppose I have a large Java project with a bunch of classes that are “helpers” for other classes, and follow a naming convention of ClassHelper, for the class it is helping. I want to find them all, even though they are scattered all over my project:$ -name *Helper.java
And my army of trained minions dutifully responds:Ende der Inhaltsvorschau. Der weiterere Inhalt dieses Abschnitts ist hier nicht einsehbar.
Zurück zu The Productive Programmer

