What does it mean to be a professional software developer at an agency?
If you’re familiar with some of the big names in books about software design - books like Design Patterns, Code Complete, Domain-Driven Design - you may have noticed that they all share a particular assumption: you are working at a product company, on a single codebase, and there is a good chance you will be working on that same codebase five years from now. As it happens, I work at a software agency, and have yet to spend more than six months on a single project. At work, we’ve been running a book club, and this discrepancy has made it hard to find books that feel relevant to our situation.
It’s not that the advice in the classics is wrong. They give you the tools to build a coherent mental model of what good software development looks like, and a lingua franca to communicate that mental model with other developers. Where they start to fall short is around issues of time - both time to implement and how long the final product is expected to last and be maintained. Because of these differences, the principles that inform my sense of craftsmanship and professionalism don’t quite match what I find in these books. Many of my priorities share the same names, but they’re definitely in a different order, and some need a few tweaks to their definitions.
That being said, I’ve been on the other side of the fence. Most of my career has been at product companies, and I’ve implemented my fair share of factories, strategies, and repositories. My goal here is to clarify my thinking on what Good Work looks like in my new environment. To that end, I’ll describe a couple common priorities (maintainability, reusability, orientation, and documentation) and how they manifest differently between the two worlds.
When listing qualities of a well designed codebase, maintainability is typically priority number one. Unless they’re carefully managed, adding new features to aging applications can take longer and longer. At a product company, this trend results in a stale, outdated application that no one wants to use. At an agency, a slowdown in velocity can start to feel like a bait and switch to the client. The main difference between the two is that the timelines are different. While a successful product team can work on the same codebase for years, most agency contracts have a finish line. A point where you stop working, put down your keyboard, and tell the client: “call me when you need a v2.”
So maintainability means something different. It means that when the client calls you in a panic about a production outage, the fix is simple. Or ideally, that the client never needs to call you at all. It means that when the retainer or follow-up contract is signed, this project isn’t a punishment for whoever it gets assigned to. The means to accomplish it are also different. KISS becomes the guiding, if not the only principle for software architecture. No more soaring cathedrals with flying buttresses of brilliant insight - simple, understandable building blocks lay the foundation for delivering on time and on budget.
Dependencies are an area of maintenance where all the same trade-offs exist between both environments, but they cut so much deeper at an agency. Saving time using an already existing library becomes that much more critical, but the update burden can be that much more painful. It’s easier to keep up to date with rolling changes to a dependency if they’re addressed as they come in. What’s more likely to happen on a contract-based project is that no updates will happen for over a year, and suddenly, all the breaking changes need to be resolved at once to capture a new feature or security update. Think carefully about your dependencies, and use them as the powerful tool they are.
Again, reusability is still a priority, but it starts to mean something different. One of the things that distinguish an agency is that your company and your team don’t necessarily own the code that you write. Instead, that ownership is assigned to the client. As I mentioned in the previous section, project timelines are shorter, and combining those two facts means that opportunities to strictly reuse code are fewer. Instead, reusability implies the reusability of skills. Identifying common skills between clients and projects, and developing those skills can have a significant payoff down the road.
Some of the skills that transfer are technical - how to get the most out of SQL, structure a redux app, work with, and avoid the pitfalls of docker. The caveat with technical skills is that unless you’re reusing the same tools between projects (better hope the client doesn’t already have a codebase they want you to work on), it’s easy to end up with a library of tools you’ve only ever used once. While a few technologies have stood the test of time (SQL comes to mind), identifying common patterns and concepts (event sourcing, virtualization) will pay off better in the long run.
Soft skills will generally pay off more as you transition between clients and projects. Never mind that client communication is now a core function of your job; there has never been a project that wouldn’t have benefitted from better requirements gathering, better estimates, better documentation. Additionally, responsibilities that traditionally fall to a project manager are more likely to end up on your plate. Some projects won’t have one, or someone from the client organization will fill the role, and your ability to plan a sprint, proactively identify and eliminate blockers, and track overall project progress will come in handy.
Investing in learning reusable skills will pay off more than planning for all the opportunities to reuse a class or function that never come. That’s not to say that modular code isn’t still a good thing, but maybe something like the Rule of Three should come into play even more than it otherwise would.
The ability to rapidly orient a new developer to a codebase is undoubtedly a feature that no one at a product company would consider a bad thing. After all, they do hire new developers from time to time, integrate members from other teams, or sometimes put things on pause while a different project needs some attention. But at an agency, this is almost priority number one. The non-permanence of an agency developer is practically a part of the definition. At product companies, it can be acceptable to take as long as three months for a new FTE to be considered “productive.” If I’m working on a 6-month contract, that doesn’t work.
There are two facets of orientation that should be addressed separately. First, is the skill of an individual developer orienting themselves to an arbitrary codebase. Mostly this will come with experience, but knowing common software patterns and various tools and techniques to explore a codebase are helpful here. Further, knowing the right questions to ask non-technical domain experts and the humility to look ignorant in front of strangers by asking those questions is essential. Secondly, leaving a codebase in a place where other developers can rapidly orient themselves is probably the highest mark of professionalism. Is there documentation? Is there useful documentation? Are there tests? Are they the right tests? Does the code follow the popular style and idioms of the domain and language? Very frequently, the next developer to touch a project will have no one to give them a welcoming tour when they get started.
A subset of orientation, but worth mentioning on its own merits, is documentation. Again, no one at a product company is going to tell you documentation is a bad thing, but you will almost always have a human being you can ask questions, and that knocks written documentation down several pegs. As such, it can be a chore that no one ever gets around to or the busy work that gets given to an intern (so it contains only the information obvious to an intern). Good documentation can be a lifesaver when you’re handed a project where the original developers left three years ago, and by the way, it’s broken, and we need you to fix it by tomorrow.
This leads to the obvious question - what is good documentation? It’s certainly not measured in quantity. If I can’t read all of it before my deadline, it’s doing me no good at all. For me, the most important thing is clear and concise startup instructions. What do I need to do to get this codebase running in a development environment? What do I need to do to build and deploy it to a production environment? For some classes of problems, this could be the only information I will ever need. If the documentation needs to go further, I have a strong preference for “executable documentation.” This is anything beyond plain text that can be verified as accurate by actually running it. Validation schemas, expected and typical use cases in automated tests, even a well structured CI pipeline can be considered executable documentation. These guard against the common documentation failure mode - written once, and never touched again, even as the code moves on.
A side-benefit to documentation is that one of the best ways to understand something is to go through the arduous process of describing it with words. This benefit is the motivation behind Amazon’s famous requirement of six-page written documents instead of PowerPoints. Putting words to fuzzily defined concepts will force mental clarity and potentially expose areas of conflicting logic or incorrect assumptions.
These ideas would all resonate with long-term developers at a product company seeking to be described as professional. But I think it is unlikely that such a developer would consider them the most significant ideas. Or define them in quite the same way. I’m calling on other agency and contract developers - we need books too. We need to commiserate on the shared experience of being called in to rescue a codebase that had previously been sourced to the lowest bidder. We need to share strategies for grokking the mess left behind by the employee who automated their entire job in excel that we’re expected to productionize across the whole team. We need a shared definition of professionalism, and a library to back it up.