I have days and even weeks when working code just bursts onto the screen with seemingly no effort and I pop out of bed the next morning ready to go again (drives my wife up the wall). Unfortunately, there are those other days when it feels like you just cannot make things move and I feel drained and burned out at the end of the day. It’s an axiom that “mama always said there’ll be days like this,” but what if we can just pay attention to what makes the good days good, the bad days bad, and use what we’ve learned to change our environment and habits for the better?
- I’m being subjective about what I’m calling “productivity” and I know it. Give me any kind of pseudo-scientific sort of metric and I’ll happily shoot holes into why it’s an imperfect measure*
- Yes, you should optimize the whole, it’s not just about cutting code blah, blah, blah, preach, preach, preach, “I have people skills, don’t you understand!” Actually, I think that’s all important too, but I’m making the assumption here that development really does mean coded/tested/approved rather than the imaginary “code complete” status.
Quick Twitch Development
I think I’m far more productive when I’m able to make very granular commits several times an hour with a clean local build for each commit. Granted, this could be interpreted as just an impression of productivity, but I think there’s some reality to the micro-commits as an indicator of productivity. Correlation is certainly not causation, so let’s work backwards and see what’s typically the situation when I’m able to do “quick twitch development:”
- I’m coding within an isolated codebase and process. Writing any kind of code that crosses process boundaries, code repositories, machines, or even just major subsystems within a big system can be much less productive in my experience. More on that later.
- My development tasks need to be small so that I can quickly flow from unit test to completed code to the next task
- My unit tests and the build script in general needs to run quickly so that I have a short feedback cycle between writing a bit of code and knowing that it does what I want it to do
- My unit tests tend to cover small areas of the code and achieve small goals such that it’s rare that I need to use a debugger to understand and solve problems
- I need to understand my problem and technical domain well enough to be able to quickly identify the tasks and steps in whatever user story or feature I’m trying to build. I’m naturally going to be much slower when I have to get out my notebook to doodle UML or CRC cards, go to the whiteboard with a coworker, or step away from the keyboard just to think my way through the problem.
So what can we do to make development be more like the list above? As much as I scoff at much of the hot air devoted to Domain Driven Design, paying attention to the idea of “Bounded Contexts” and trying to do most of your coding work inside one context can help. For my part, I’ve tried to organize the FubuMVC ecosystem into more cohesive repositories and solutions to get smaller codebases where the unit test cycle and automated build cycles become much, much faster — and after the dust settled down from the churn, I’d argue that I see a much higher throughput.
As far as being able to work in small, atomic tasks, I cannot strongly enough recommend the pursuit of Orthogonal Code. The end result should be faster unit testing feedback cycles and smaller coding tasks. Working with monolithic blobs of code tends to make tests slower, harder to write, and more likely to push you into needing the debugger more often.
If you’re new to a project, I think you’re going to have to invest some time looking through the codebase to understand the organization of the code, the coding style the team uses, the key abstractions, and the way that responsibilities and roles are assigned within classes or functions of the codebase. Doing this can help you be more successful in breaking down bigger coding goals into small, achievable tasks and unit tests.
My Personal Involvement with the “Goal”
Years ago I read an article from Joel Spolsky telling us about how wonderful their requirements process was for FogBugz. I rolled my eyes and thought to myself “of course you’re doing a great job, you’re building a system you use yourself to solve your own problems. Let’s see you do that at my job where you’re working in a domain you don’t know.” My sarcasm aside, I think there’s a lot of value in thinking through this statement. I know that I’m far more productive in projects where I’m:
- Heavily invested in the success of the project (like, say, making the FubuMVC 1.0 release in January)
- Deeply knowledgeable and somewhat enthusiastic about solving the problem (this is why “Shadow IT” is so prevalent and even successful in big companies where much more qualified IT personnel struggle to complete the same projects)
- Getting a lot of active collaboration from the real domain experts. I can happily derive enthusiasm for a project if the project stakeholders themselves are enthusiastic about the project and heavily engaged. My most successful project as a professional developer was a technical mess, but the domain expert spent a lot of time with a very green technical lead and helped create a strong vision that did actually make a real difference when we rolled it out to the factory floors.
On the other hand, things just won’t go that well when nobody really believes in the project, the theoretical business partners won’t interact much with you, and it’s “just a job.” I don’t know what it’s like where you’re at, but the job market in Austin for developers is so hot right now that I think you’re crazy for staying in a job you don’t like.
I understand the terrain
When other developers ask me “what should I learn?,” I will invariably advise them to concentrate first on technology agnostic software fundamentals and only learn technologies or frameworks as you need to. However, there’s something to be said for having a deep understanding of the technical stack, languages and tools that you’re working on in any given time.
The sad truth is that in any given set of tools there are going to be plenty of times when you go off the rails, be it “Ruby can’t find this gem, .Net can’t resolve this assembly version, or the dreaded ‘you need new Guid’s from long ago.'” At a stand up meeting a couple years ago, one of my colleagues was struggling with a null reference exception coming from NHibernate on startup.* I correctly guessed the root cause and helped him fix it quickly only because:
- I just happened to know that NHibernate threw that NullReferenceException as a side effect of a configuration key being missing
- I knew that the code he was using was being executed from an isolated AppDomain
- I know that when you programmatically spin up a new AppDomain you usually (always?) need to specify what the configuration file is for the app settings
As much as I think that conceptual ideas like design patterns and design fundamentals are more valuable on the whole than technical trivia, I would have been at a complete loss if I didn’t understand the inner workings of the tooling we were using at that time. On the other hand, I’ve struggled mightily when I’ve had to work with technologies where I’m not as familiar — the last time I coded on the Java stack, x509 certificates, the first week we used RavenDb last year.
There’s a lot more to talk about, but my battery is almost dead and I have to take my son to go play Laser Tag for his birthday. The single biggest thing that drags down *my* productivity is dealing with development environment dependencies when those upstream dependencies are changing underneath me. I’m going to promote that to its own blog post.
* My personal metric of coding production is the number of unit and integration tests for a codebase, but it’s only useful in blocks of 25 or larger over significant lengths of time.
** “But Jeremy, that was probably just the case of a bad exception that should have been fixed in that API.” And I’d say that I agree with you and you should endeavor to make exception messages as clear as possible when you’re writing API’s for other developers. That said, it’s an imperfect world — especially if you’re gonna go off and use someone else’s code.