You're Doing It Wrong! And Other Truisms of Software Development
To paraphrase Fred Brooks: Programming is Easy, Systems are hard. While the construction of any software product may start out as an exercise in programming, once it is successful enough to stick around, it will evolve into a complex system governed by complex and poorly understood rules.
This has led the computer science profession on an unending quest for solutions to tackle and try to mitigate this system-level complexity.
The cynic might even say that we’re in a sad cycle that’s destined to repeat itself ad infinitum. C++ was developed with an incredible expressive but complex OOP syntax to try to rein in the free-form complexity of the large procedural C systems that were being built. Then Java, with its rigid OOP rules was designed to rein in the exploding variation and complexity of syntax that C++ brought about and make it so that every mid-level developer could write and understand the same OOP code. Then Google developed Go to move away from the rigidness of an OOP-only language back to a … more procedural approach with the express goal “to eliminate the slowness and clumsiness of software development at Google, and thereby to make the process more productive and scalable.” .. And so we’ve come full-circle* and have yet to find that silver bullet (and no, it’s not Haskell.)
The problem is that systems complexity is a System level problem and so the problem itself depends on the system in question, not the tools used to build it. There can’t be a silver bullet if what we’re actually firing is a Ballista.
And so, this brings us to the common refrain often heard in some variation in all places where developers congregate online: “You’re doing it wrong!”
Building a large SaaS platform on Ruby on Rails? “YDIW! Rails can’t scale!” Building out a SaaS platform on Java? “YDIW! Java is too slow to code in and too expensive to deploy!”
Keeping your code in a monolith? “YDIW! Microservices are the only solution to keep moving fast!” Building out a microservice architecture? “YDIW! Your deployments, error checking and development environments are much more complex!”
Using multiple repositories for your Apps? “YDIW! Sharing your business logic is much more difficult and you should be using a Monorepo!” Using a Monorepo? “YDIW! You’ve got multi-hour CI and one developer screws something up and the whole company is screwed!”
YDIW is a truism in that there are situations where it may be obviously be true, but without digging a lot deeper in the situation, saying it doesn’t bring any value to the table. The right approach for a system depends on the system.
If I’m solo building a toy project, then I likely need a completely different approach to the codebase, mechanism and abstractions I use than if I’m collaboratively building a project with 100 other engineers. Multiple layers of adapters, facades, and large black-box subsystems would be a massive over-architecture in the toy project (MVC may even be overkill), but those layers are essential to keep up the pace of progress on the larger codebase.
So what do we do at Alignable? The answer, as you might guess, is that it depends on the situation. We’ve got a Majestic Monolith that runs the majority of our website and iOS app. We’ve got a few services extracted where a clean break in business logic and access permissions make sense and that gives us some additional flexibility on hosting and deployments. We’ve got standard tools and patterns that we try to stick to make it easier for developers to read code in disparate parts of the application, while at the same time we skunk-work new tools and patterns to keep our best-practices up to date.
We work intentionally to manage and avoid future complexity by following the principles of YAGNI and DRY. We try to move as much responsibility for understanding our complex systems out of developers’ heads and into the codebase, allowing our CI environment and test suite help catch unintended consequences and regressions. We’ve built an automated PR-tool called Checkie (Open-source release coming soon) based on concepts from Atul Gawande’s Checklist Manifesto to make sure we get the simple things right.
Are we doing it wrong? Probably we are for a 200 developer engineering team, or a 2 person team. But for our 15-ish developers that we want cranking out reliable production code without slowing down as the system grows, I think we’ve found our sweet spot for now and recognize that our approaches and patterns will need to evolve as the company grows and the product and scale evolves.
- This is overly a simplistic view that ignores significant advances in developer happiness, garbage collection, tooling, security and concurrency that have come over the last 50 years, but I stand by the general conclusion.