Me: Chat GPT write me an intro about principles that avoids making obvious statements, listing the dictionary definition of principle, or regurgitating someone else's intro
Chat GPT: Sorry, I cannot do that. As a language model, I lack the ability to do anything other than clever regurgitation and recombination, or a restatement of scraped information.
Me: Well, bummer.
Me: Okay, Chat GPT write me a segue for this conversation into a blog post about principles that is mostly a list for me
As I get further in my career, I'm starting to find that across different organizations, different tools, different types of software, it's easy for me to lose track of what exactly my engineering principles are and what I find important. Earlier in my career, it was easier to keep these sort of nebulous and to myself because my day-to-day was centered around code and delivery of that code. I was less involved with the totality of the code, and more with what I was writing for a particular piece of work. These days, I'm often either working to define some strategy to be implemented with my team or working with other engineers to define strategy that will be implemented by many teams over an extended period of time. I'm still in the code, but I'm not in everyone's code and there are many more people and opinions around to consider.
This post is primarily for my future benefit as, sometimes, I need to be reminded that I have opinions too and experiences from having built some good software (and plenty of bad software) and that I need to advocate for those! I'm skipping over many of the principles that many would consider fundamental, generally good practices (e.g. SOLID for OOP), and sticking to those I feel strongly about.
Here's my MySpace Top 8 of these principles and why they matter to me:
Naming is everything.
I think this has maybe started catching on much more with the increased popularity of domain-driven design and "ubiquitous language", but it's long been joked that naming is one of the hardest problems in computer science. For many things, naming is incredibly important to get right, as changing it can be expensive after a certain point (e.g. domain objects/concepts, database tables, identifiers) or a painful nuisance (API fields, interfaces). I think about names a lot before I commit to them. I think about all the times I've seen a "we should rename this" comment in something written 5+ years ago.
Interfaces tell the story. Tell a good story.
Designing good interfaces and APIs is closely related to naming, but there's a broader goal here around being able to understand what something does by looking at its interface. What are the methods on the object / functions in a module? Do they all make sense together? Can I figure out what this object or module is for by reading them? How does the object or module interact with the rest of the system? Do the data structures make sense as data or do I need to know a lot about the implementation? Do conceptually-linked functions feature similar signatures and work on the same types? Can I predict the name of a method, function, or data type I might need without going back to the docs?
All of these are core elements of telling the story of what, where, why, and how. Hopefully the who... is you! (I'm sorry, couldn't resist.)
Solve it simply first / do it the dumb way before doing it the smart way
This is basically a combination of Gall's Law ("A complex system that works is invariably found to have evolved from a simple system that worked.") and "You Ain't Gonna Need It" (abbr. YAGNI).
Doing things simply is better than doing things complexly. Most people probably don't start out doing things complexly, but there's certainly engineering organizations that worship complexity on some level and so it's important to remember that its not something to emulate if you want to have a good time writing software. Software is all about managing complexity, so there will of course always be some complexity. Just try to keep it related to your domain and interesting complexity you should be solving, and not "how do I turn this reasonably sized service into 30-40 serverless functions I have to orchestrate". Offload complexity where you can.
Doing things complexly also has a tendency of taking a long time, and you shouldn't take a long time to do things you don't know if you'll need. Sometimes that means doing something really simple and stupid to validate that the idea is even worth doing properly. At the very least if it fails, you didn't spend a lot of time writing it and you still get to delete something. Win-win.
The continued existence of both Unix and Windows is a paradox that both validates and invalidates this entire principle.
Boring tools over new shiny tools
New shiny tools are exciting, but play with them on your own unless they solve a real problem you have and there's isn't a better supported existing tool or one you know better. Playing with new shiny tools is fun to do at a hackathon, post about in the blogosphere, hack on late in the evening, talk about at a meetup, etc., but most shiny tools come with the burden of educating your team on it, tying your application to something that hasn't yet proved its longevity, and significantly fewer support resources for when you encounter problems. There's also the inevitable V2 release that solves everything with V1 but breaks every API in a way that is most inconvenient to your application specifically.
There's always exceptions to this, and there's differences based on who's making the new shiny thing, etc, but if you're building a product that isn't "use new shiny tool dot biz" it's best to stick with what you know.
tl;dr: Just use Postgres
Design for easy deletion
This could also be called something like "design for re-usability" but that's boring and can easily encourage the wrong thing. It's fun to think of all the ways something might get re-used but there's a lot of stuff that won't ever get re-used. I like to think about designing for ease of deletion, because there's nothing better than confidently deleting something you know isn't being used anymore and can easily remove because it's not tightly coupled to a bunch of other stuff.
The advice for either re-usability to easy deletion is the same: write Good™ interfaces, separate concerns/build it as a library, etc.
Duplication is better than the wrong abstraction
This is sort of the flip-side to the one above and one of the many lessons I took from Sandi Metz, who coined the phrase. I think every programmer has a phase in their career where "DRY" is gospel and any duplication must immediately be vanquished. There's a lot of good reasons not to do this from "that seems like a lot of work and it's only in three places and they're not related" to "oh god why are there so many nested conditionals to build out this copy string". But the best reason is that sometimes stuff just isn't super well-defined yet and a little duplication while you're figuring things out is not such a naughty thing.
A big reason DRY caught on is that it comes from an era where tooling was less mature, certain disciplines were less mature, and people were trying to avoid ever having to update inline styles on 150+ individual HTML files that contained the same layout boilerplate. DRY is a good goal of course, but it is mostly telling you to use a template engine appropriately.
Have a point-of-view
a.k.a. the thing I remind myself: "have a fucking opinion!"
Opinionated software is software that has a clear point of view as to what problems its solving and behaves consistently. Maybe it provides some knobs, but generally things should have well-paved, blessed paths, and not a build-your-own Smörgåsbord for different use-cases (unless of course you're building Smörg.ly, the Smörgåsbord of enterprise market analysis).
Sometimes this means saying no to people who suggest or want particular changes. You or your team has probably spent a lot of time thinking about what you're making, so don't be afraid to say no (politely) if it doesn't match the point of view you're software is taking.
If someone has a differing point of view for what they think the software should be doing, they can either convince you or make their own software. It's fine to have different ways of doing things! (Obviously, this works differently in open source vs. at a business.)
Pragmatism over dogma
Hey, that opinion thing I said? Yeah, turns out a lot of people also have those. Sometimes you need to just agree on something, get on-board, and ship something Deliver User Value. We're trying to solve problems here, not win a theological debate. Solve the problem.
There are reasons to put your foot down and wrong ways to go about solving problems, but many things can be revisited.