Click the image below to get $75 off Ted Young’s new interactive online class running March 8th-11th 2021. Use special code "GEEPAW" at checkout. Thanks!
Listen to this Blogcast:
"It puts the database on its browser skin, or else it gets the hose again." This task occupies the daily life of a great many programmers. Today, I want to throw out some random sparks of advice for people working in that setting.
Folks, my ideas about changing code are deeply intertwined with my ideas about changing the world, which is a far more important activity than any kind of geekery. Let’s geek out, for sure.
But please keep working for change, and supporting those who do. Black Lives Matter.
In enterprise IT, it is commonplace for backend folks to work on problems shaped like this: there’s a web endpoint controller on the top, a database on the bottom, and some simple or complicated business logic in the middle.
I watch people working in this kind of environment quite a bit, and I have to tell you, most of what I see is them having a lousy time doing boring work in a tedious and time-consuming fashion.
These ideas I want to toss at you are something of a grab-bag. They’re just little seeds, not worked out answers, but my goal in offering them is to jiggle a mind or two: to get folks thinking about how they could do this kind of work in a way that wasn’t a boring drag.
And I spoze that’s the meta-seed:
"You don’t have to live like this."
When I work with teams, I am often struck by how powerful their sense that whatever they’re doing now is how it’s always done, everywhere, for all time. I call it the Infinite Now or the Infinite Here.
In fact, the reason we build things the way we build them is nearly always because we choose to build them that way. If we’re not happy with that, well, hell with it, let’s build them another way, and see if that’s better. Change-harvesters say:
Try different, not harder.
So here’s some pot-shots. They don’t form a coherent thread. Some of them are gonna sound pretty "should-ish", but please just accept that as shorthand. I’m not much of a should-er, and am even less of an always-never person. You may have to lean toward me a little.
Potshot: Don’t use the browser to test anything you don’t absolutely have to.
In particular, don’t use it to establish that your business logic is correct.
Browsers are slow. Humans clicking on them is slow. Http is slow. Starting servers is slow. And the thing is? Looking at browsers is imprecise & un-repeatable. Sure. Check the browser. When you have to. But don’t prefer it.
Potshot: Don’t let controllers think about the problem domain.
A controller’s job is to get the right input to the right place, stripping http-isms on the way in, and returning the right output to the right place, adding http-isms back in.
That’s already a lot of responsibility. If you also have them making decisions about, idunno, insurance, or future orders, or customer addresses, you’re adding even more responsibility. You’re also forcing yourself to test http-translation and business logic at the same time.
Potshot: Don’t use transport error codes to indicate problem domain issues.
Those are for transport errors. If you need to reflect domain errors, include domain error fields in your return packet.
You will need to include domain error fields, so you might as well start every return message with one. I generally throw in text, as well, because there are often "variants" of a particular error. In particular: empty search results from well-formed queries aren’t 400’s.
Potshot: Finding a problem by inspecting the log is the first step of a process whose second step is always making it so you never have to look at the log for that bug again.
Logs are terrible debugging tools, just terrible. We have to use them from time to time, but never twice.
I’ll go strong on this one, even tho I might get yelled at: Mastering the art of reading the logs is an indictment of your process, in the same sense that getting good at resolving merge conflicts manually is.
Potshot: You don’t have to have 1-to-1-to-1-to-1 across your layers.
Architecture isn’t a prix fixe menu with choices A-D. It can be 34-to-2-to19-to-5, if that’s what it takes to make small testable objects do the work.
The one-class-per-layer stack creates some classes that are gigantic and some that are no-ops, and neither is desirable. If it takes 15 tiny classes to compose your solution in this layer, that’s what it takes, and it’s not just fine, it’s excellent, because you can test them.
Potshot: Implementation inheritance will slow you down, especially when you’re inheriting from somebody-elses-code.
If you don’t have to, don’t do it. If you do have to, do it and use it strictly as an adapter.
A particular source of woe in ’90s style codebases: every class you write is derived off a class you don’t own, can’t test, and in many cases, can’t even be used without running the server or a special heavy-weight test rig.
Potshot: Circling nouns in the problem statement is a fine way to start a design, but most of the time it’s insufficient.
You need specialists, agents or investigators, too. They won’t be in the problem domain already, you’ll have to make them up.
If you restrict yourself to only using domain language, you’re going to create gigantic classes. An insurance processing system uses a Patient for thousands of actions, for instance. Specialist agents that operate on Patient are likely to be far superior to any other scheme.
Potshot: Don’t let business logic think about things other than the problem domain, in particular, about databases and tables and rows oh my.
This is the analog to not letting controllers think about problem domains.
Business logic that has to know table names and where clauses can’t be tested without a database. Testing through the database is just as slow and tedious as testing through the controller, and almost as imprecise and unrepeatable.
Potshot: If you use the repository concept, use outward-facing custom repos, not transparent CRUD.
A repository isn’t a generic abstraction of a domain concept, it’s a tool we use to service our business logic.
In particular, if you don’t need datum X to perform action Y, don’t use a repository that gathers it. Feel free to multiply repos per task. Make them give you custom variants in custom variant collections. Your code will skyrocket in clarity and performance.
Potshot: Don’t mix vetting data with processing it. Decide whether it’s okay to do a thing before you do it.
Untangling the results of running your process on bad data leads almost directly to a padded room. Test the decision & doing separately.
A massive amount of labyrinthine code is created by folks who are processing the data at the same time they’re deciding whether it’s valid to process it. I’m telling you, down this road lies great woe.
I’m exhausted, and I feel sure I haven’t covered everything on my potshot list. But I just want to say that meta-potshot one more time.
You don’t have to live like this.
If your style of development feels unpleasant, it’s likely also unproductive. So? There isn’t one way to code an enterprise web-to-database system. There’s tons of them. If you don’t like working like this, don’t work like this.
I hope you enjoyed the random list. I hope even moreso that it perturbed you a little. If I’ve created a disturbance in the field, well, that would just about perfect. 🙂
If you love the GeePaw Podcast, consider a monthly donation to help keep the content flowing. You can also subscribe to get weekly posts sent straight to your inbox. And to get more involved in the conversation, jump into the Camerata and start talking to other like-minded Change-Harvesters today.