const slow: Promise<string> = sleepPlease(500).then(() => “yay finally”); I imported promisify from “util”. setTimeout is built in, but its arguments are in the wrong order to naturally pass to promisify.
Tradeoffs are a fact of life. Choose between safety and speed, stability and flexibility. More of one sacrifices the other.
I remember a time when this described our decisions about software deployments. Deployments caused problems, so we did fewer of them.
Then came Continuous Integration and Continuous Deployment. With a lot of work, we get automated tests and monitoring, automated deployment and rollback. Then we learned that small deployments are safer — so much safer that many fast deployments added up to less danger than one bulk deployment.
Fast and Safe
It’s like, this automation plus culture change has folded space — these arrows now point in the same direction.
Bending tradeoff space like this wasn’t free.
Tests double our application code, and infrastructure adds even more code. Harder: we had to change the culture. Think in smallest shippable units. Alleviate the next bottleneck after deploys aren’t it, and the next.
Without weeks of manual testing, we had to create new ways to answer “Does it work?” Without ops people standing by, we needed new ways to answer “is it up?” When each person makes deployment decisions individually, they need tools to support the decision with visibility, and tools to implement the decision with fast rollback in case of surprise.
This automation is expensive, but we wouldn’t go back to the old days of dangerous, slow deployments.
Flexible v Consistent
I want to talk about another tradeoff space, this one in “Do we create custom internal software?” As a business, I want internal software to increase the consistency, legibility, and speed of my employees’ work. We think of the cost of software in terms of money and time, work to create it and to keep it happy. There’s a deeper cost to the organization, though: ossification.
The consistency enforced by software is great for managers and administrators; they get legibility, meaning they can track work at a high level. These are the people making decisions about software requirements.
I once worked on workflow tracking software for a research organization. The heads of the development effort bragged that they’d encoded the knowledge of the scientists, so they could leave and we’d still have it. The research could proceed… in exactly the same way. Any substantial change took (this is extreme, but it was ten years ago) eighteen months to roll out. So much for scientific innovation! oh but it was great for the higher-ups tracking progress of each experiment as it moved through this regimented system.
This consistency is not optimal for learning, not for daily patient care or customer service. People are adaptable, but software inhibits this adaptation. Ask any nurse, social worker, or hotel receptionist how they feel about the software they have to use. (Ask them when they are off duty.) They’ll tell you about frustrations, time wasted, obstacles to doing great work.
Compared to a paper form, an electronic form is constraining. Compared to talking to each other, coordinating via task trackers is limiting. Software ossifies processes. It impedes learning and improvisation.
Flexible and Consistent
How could we fold this tradeoff space?
This tradeoff exists when the people using the software can’t change the software. Maybe it can help to look at one place where this tradeoff does not exist, where it is naturally folded.
When the users are also the system movers, calcification doesn’t happen.
Software teams developing for themselves gain consistency, legibility, speed and more — while adding flexibility. When deployment is automated in one place, we can improve the process without the work of disseminating information.
The other day in the kitchen, I was disgusted by the knife left in the sink. Knives are dangerous. Wash them, dry them, put them directly back into the knife block.
It’s just one thing in the kitchen. It’s one that I’ve picked to be important. Meanwhile I left bits of food on the floor, kicked them under the edge of the counter. Someone else would find that disgusting.
i can let go of my fury about the knives now. And laundry baskets on the couch (those things sit on the basement floor, ew). And ripped-open envelopes, those are so hideous I must throw them away immediately.
These are only my pet peeves. The other adults in my household are not deficient people for prioritizing something else instead. There are infinitely many useful things to do or clean. If they do choose to put the knives away or trash the envelopes for my sake, it is a gift.
It’s the same in code. I have my pet peeves. How important is it that the tests hit live services instead of faked ones? Yes it will be faster and better when we put in the work to change that, but there are tons of other things to work on too. It’s a balance. Sometimes we do a thing because it makes the other people on our team more relaxed. Christian doesn’t like abbreviations so I’ve started writing out “repository” more. These are gifts we can give each other to smooth our teamwork.
My dentist tells me that taking great care of my teeth is essential for health. My chiropractor thinks my spine is the #1 thing I should keep in order. (Well, my former chiropractor; I didn’t revisit that one after the guilt trip.) A personal trainer will say exercise is a top determiner, and a dietician that food is first.
They’re all right; all these are important. But I can’t do everything! I have work to do and a life to live!
It’s a balance. At each moment one of these things matters most; if my back is wonked then chiropractic care will help. If my energy is super low, am I low on iron or need more exercise? Life isn’t about doing everything right; it’s about getting the right things a little more right. And “the right things” are extremely contextual.
In software, people exhort developers to be responsible. Write tests first. Make good abstractions. Create a universal language. Experiment and validate. Retrospect and follow up. Refactor. Read everything in Slack, and all the commits, and emails. Get the user docs beautiful. Add null checks and error messages. Unit test and property test and integration test and simulation test. Learn the business and the code and the tech. Alphabetize your imports.
If I wrote code Correctly, it’d be useless by the time I shipped it. (qualifier: I’m talking about business software that’s unproven, not serious libraries.) Oh yeah, and ship constantly.
It’s contradictory! So you know for sure it’s contextual, that some of these rules don’t matter in your situation.
There’s several ways that my team is doing it “wrong.” There are several dirty things in the pull request I’m about to submit. The question is, is it good enough? and which improvement will make a difference in this context?
We tend to improve the things that stand out to us, which may or may not matter. Code smells, we know how to spot those. But what’s the leverage point, what’s the constraint in the system? Maybe it is a few missing tests. More likely it isn’t software at all, but communication among team members. Or my understanding of the business.
The magic solution for one team is a waste of time for the next. Consider your context carefully, and don’t let people guilt you about the tasks you consciously chose not to do right now.