> The kinds of topic being discussed are not "is DRY better than WET", but instead "could we put this new behavior in subsystem A? No, because it needs information B, which isn't available to that subsystem in context C, and we can't expose that without rewriting subsystem D, but if we split up subsystem E here and here..."
Hmm, sounds familiar...
Bingo knows everyone's name-o
Papaya & MBS generate session tokens
Wingman checks if users are ready to take it to the next level
Galactus, the all-knowing aggregator, demands a time range stretching to the end of the universe
EKS is deprecated, Omega Star still doesn't support ISO timestamps
In 30 years in software dev, I am yet to see any significant, detailed and consistent effort to be extended into design and architecture. Most architects do not design, do not architect.
Senior devs design and architect and then take their design to the architects for *feedback and approvals*.
These senior devs make designs for features and only account for code and systems they've been exposed to.
With an average employment term of 2 years most are exposed to a small cut of the system, which affects the depth and correctness of their design.
And architects mostly approve, sometimes I think without even reading the docs.
At most, you can expect the architects to give generic advice and throw a few buzzwords.
At large, they feel comfortable and secure in their positions and mostly don't give a shit!
I've been thinking about this a lot. 2~3 years is a long time, long enough to have a pretty good grasp on what a code maintained by 50~100 does in pretty concrete terms, come up with decent improvement ideas, and see at least one or two structural ideas hit production.
If the person then stays 1 or 2 more years they get a chance to further refine, but usually will be moved up the ladder Peter Principle style. If they get a chance to lead these architecture changes that company has a chance to be on a decent path technally speaking.
I'm totally with you on the gist of it: architects will usually be a central switch arranging these ideas coming from more knowledgeable places. In the best terms I see their role as guaranteeing consistency and making sure teams don't impede each other's designs.
> For instance: In large codebases, consistency is more important than “good design”
But this is exactly the type of generic software design advice the article warns us about! And it mostly results in all all the bad software practices we as users know and love remaining unchanged (consistently "bad" is better than being good at least in some areas!)
I don’t know. At my place a lot of cowboy engineers decided to do things their own way. So now we have the random 10k lines written in Redux (not used anywhere else) that no one likes working with. Then there’s the part that randomly uses some other query library because they didn’t like the one we use in 95% of the code for some reason, so if you ever want to work with that code you need to keep two libraries in your head instead of one. Yes, the existing query library is out of date. Yes, the new one is better— in isolation. But having both is even worse than having the bad one!
Ugh I remember a "senior" full stack dev coming to me with various ideas for the backend - start use typeorm instead of sequelize and replace nestjs with express, for the tickets they would work on, despite having no experience with any of these. The mess of different libraries and frameworks they left in the frontend will haunt that software for years lol.
So following that silly comic you'd ban utf-8 because it breaks consistency? (even though in reality it beat most other standards, not just became 15th)
This isn't really about software quality, it's about the entire organization.
Consistency enables velocity. If there is consistency, devs can start to make assumptions. "Auth is here, database is there, this is how we handle ABC". Possible problems show up in reviews by being different to expectation. "Hey, where's XYZ?", "Why are you querying the database in the constructor?"
Onboarding between teams becomes a lot easier, ramp up time is smaller.
Without consistency, you end up with lots of small pockets of behavior that cause downstream problems for the org as a whole.
Every team needs extra staff to handle load peaks, resulting in a lot of idle devs.
Senior devs can't properly guess where the problematic parts of fixes or features would be. They don't need to know the details, just where things will be _difficult_.
Every feature requires coordination between the teams, with queuing and prioritizing until local staff become available.
Finally, consistency allows classes of bugs to be fixed once. Fix it once and migrate everyone to the new style.
My reading of it also violates the Boy Scout Rule. That is to say: if improving some portion of the codebase would make it better, but inconsistent, you should avoid the improvement; which is something that I would disagree with.
I think adherence to “consistency is more important than ‘good design’” naturally leads to boiling the ocean refactoring and/or rewrites, which are far riskier endeavors with lower success rates than iterative refactoring of a working system over time.
if you have some purported improvement to a codebase that would make it inconsistent, then it's a matter of taste, not fact, whether it is actually an improvement.
Yeah that line gave me a twitch. Reading on though it's more about the resulting coherence and correctness rather than like the Ralph Waldo Emerson quote: "A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines."
I agree. It's only the foolish consistency that's problematic. A sensible consistency does, as you say, provide a coherence. William James, who overlapped Emerson, has a lot to say about positive habits.
I have opposite experience. Consistency is commonly enforced in bigger corporations while it's value is not that high (often negative). Lots of strategies/patterns promoted and blindly followed without a brief reflection that maybe this is a bad solution for certain problems. TDD, onion/hexagonal architecture, SPA, React, etc.
"So we should all write bad code to keep it predictable?"
its true and false at the same time, it depends
here I can bring example: you have maintaining production system that has been run for years
there is flaw in some parts of codebase that is probably ignored either because
1. bad implementation/hacky way
2. the system outgrow the implementation
so you try to "fix" it but suddenly other internal tools stops working, customer contact the support because it change the behaviour on their end, some CI randomly fails etc
software isn't exist in a vacuum, complex interaction sometimes prevent "good" code to exist because that just reality
I don't like it either but this is just what it is
"Generic Software Design" as the author called it, is nice for setting the general direction of some implementation. This is why I like to read software engineering books. It's easier to solve a problem if you have some kind of framing to guide you. And it's easier to talk about the solution if everyone share the same terminology.
But yes, the map is not the territory, and giving directions is not the same as walking the trail. The actual implementation can deviate from the plan drafted at the beginning of the project. A good explanation is found in Naur's Theory of Programming, where he says the true knowledge of the system is inside the head of the engineers that worked on it. And that knowledge is not easily transferrable.
> if you come up with the design for a software project, you ought to be responsible for the project’s success or failure
I think this should also apply to people who come up with or choose the software development methodology for a project. Scrum masters just don't have the same skin in the game that lead engineers do.
"In large codebases, consistency is more important than “good design”" - this is completely opposite from my experience. There is some value in consistency within single module but consistency in a large codebase is a big mistake (unless in extremely rare case that code base consists entirely of very similar modules).
Modules with different requirements should not have single consistent codebase. Testing strategy, application architecture, even naming should be different across different modules.
There are two extremes here: first, the "architects" that this article rails against. Yes, it's frustrating when a highly-paid non-expert swoops in to offer unhelpful or impossible advice.
On the other hand, there are Real Programmers [0] who will happily optimize the already-fast initializer, balk at changing business logic, and write code that, while optimal in some senses, is unnecessarily difficult for a newcomer (even an expert engineer) to understand. These systems have plenty of detail and are difficult to change, but the complexity is non-essential. This is not good engineering.
It's important to resist both extremes. Decision makers ultimately need both intimate knowledge of the details and the broader knowledge to put those details in context.
> I don’t know if structural engineering works like this, but I do know that software engineering doesn’t.
Structural Engineering (generally construction engineering) does work like that. Following the analogy, the engineers draw; they don't lay bricks. But, all the best engineers have probably been site supervisors at some point and have watched brick being layed, and spoken to the layers of bricks, etc. Construction methods change, but they don't change as quickly as software engineering methods. There is also a very material and applicable "reality" constraint. Most struct's knowledge/heuristics remains valid over long periods of time. The software engineers' body of knowledge can change 52 times in a year. To completely stretch the analogy - the site conditions for construction engineering are better known than the site conditions for a large software project. In the latter case the site itself can be adjusted more easily, and more materially, by the engineering itself i.e. the ground can move under your feet. Site conditioning on steroids!
Ultimately, that's why I agree fully with the piece. Generic advise may be helpful, but it always applies to some generic site conditions that are less relevant in practice.
Reading that particular section made me think of the tree swing cartoon [1]. I agree that the best engineers have likely been on the ground making concrete changes at some point, watching bricks being laid as you said, but I have encountered quite a few supervisors who seemingly had no idea how things were being implemented on the ground. As the post says, people on the ground then sometimes have to figure out how to implement the plan even if it ignores sound design principles.
I don't view that as a failure of abstraction as a design principle as much as it is a pitfall of using the wrong abstraction. Using the right abstraction requires on the ground knowledge, and if nobody communicates that up the chain, well, you get the tree swing cartoon.
I agree with you. But, talk too long or too fulsomely about "abstractions" or "principles" and you'll lose the brick layers. They're paid by the course, generally. Trust them to make the site adjustments, but always verify that it's not a bad-bad-thing.
It sounds like you are making the argument that there is no established way to generate good software. If that's the case, then software isn't engineering, but rather art. The former requires established/best practices to be called a discipline, while the latter is a creative endeavour.
NASA is a bit of an outlier. In the 50's through the 70's any failure, particularly a failure involving the loss of a life, would have been a national catastrophe; a blow to national prestige. So, they were super careful that it didn't happen. The spent-cost was irrelevant compared to the reputational value at stake. Honestly, it was a wise investment given the operative quid pro quo in those days. Maybe they still do good software, I don't know, but I suspect that the value at risk today makes them more cost averse, and less sensitive to poor software.
"Business" runs the same calculations. I'd posit that, as a practical matter, most businesses don't want "good" software; they want "good enough" software.
A lot of this is because while a 'good' business is waiting for the 'good' software to be written, some crappy business has already written the crappy software and sold it to all the customers you were depending on. In general customers are very bad at knowing the difference between good and bad software and typically buy what looks flashy or the sales people bribe them the most for.
That's true. I do. I consider it a creative art, with some disciplinary adjacency to engineering. The creative sculptor has to know the material stone in order to make anything good with it. But, construction engineering is creative too; just different.
> Nah, those changes are only in the surface, at the most shallow level.
Very strongly disagree.
There are limitless methods of solving problems with software (due to very few physical constraints) and there are an enormous number of different measures of whether it's "good" or "bad".
Respectfully, I disagree. You're correct on the facts, but any "new techniques, materials and tools" need to be communicated to the brick layers. That takes time and effort i.e. it all needs to be actively managed. The brick layers have to be able to work with those new techniques and materials. I don't want some of them using method #1 over here, and method #2 over there, unless I'm wholly conversant with the methods, and fully confident that it'll all mesh eventually. The system i.e. the whole shebang has to work coherently to serve its purpose.
In the best scenario the developers are also active users of the software they produce. Then a design flaw or an error that affects the users will also affect the developers and will (hopefully) motivate the latter to correct it.
This is also the type of thing that makes having separate software architects that aren't actually maintaining the software generally a nonsensical idea.
There are too many decisions, technical details, and active changes to have someone come in and give direction from on high at intervals.
Maybe at the beginning it could make sense sort of, but projects have to evolve and more often than not discover something important early on in the implementation or when adding "easy" features, and if someone is good at doing software design then you may need them even more at that point. But they may easily be detrimental if they are not closely involved and following the rest of the project details.
There is real irony in a blog post saying you can’t trust generic advice, which is itself a generic advice blog post, and links to other generic advice they have written.
I completely disagree with almost the entirety of the article. It’s all about prior experience building large things many times yourself, not using some framework or other external abstraction.
When you have done this many times you absolutely can design a large application without touching the code. This is part planning and risk analysis experience and part architecture experience. You absolutely need a lot of experience creating large applications multiple times and going through that organizational grind but prior experience in management and writing high level plans is extremely helpful.
You’re speaking of implementing yet another system of a familiar kind, I.e. a new project. The OP says that generic design works for new projects. He’s mostly talking about designing new features to be added to an existing system, in which case the design has to be contingent on the existing system.
Software developers like to think they are special. They aren't. Software, from a planning perspective, is not much different than physical construction.
When it comes to extending an existing application it really comes down to how well the base application was planned to begin with. In most cases the base application developers have no idea, because they either outsourced the planning to some external artifact or simply pushed through it one line at a time and never looked back. Either way the person writing the extension will be more concerned with the corresponding service data and accessibility than conformance to the base application code if it is not well documented and not well tested in a test automation scheme.
I've always felt it's unrealistic to separate upfront architecture from implementation, because my experience is a lot of systems turn out to have requirements that are a lot more complex in reality than they might seem at first, even if you think quite hard about the requirements.
Imagine if you worked for an online retailer like Amazon, and you were assigned to architect a change so you can add free sample items into customers' orders. Take a moment to think about how you'd architect such a system, and what requirements you'd anticipate fulfilling. In the next paragraph, I'll tell you what the requirements are. Or you can skip the next paragraph, the size of which should tell you the requirements are more complex than they seem.
The samples must be items in the basket, so the warehouse knows to pick them. They must be added at the moment of checkout, because that's when the order contents and weight can change. Often a customer should receive a sample only once, even if they check out multiple orders - so a record should be kept of which customers have already been allocated a given sample. It should be possible to assign a customer the same sample multiple times, in which case they should receive it once per order until they've received the assigned number. Some samples go out of stock regularly, so the sample items should not be visible to the customer when they view their order on the website, but if shipped it should appear on their receipt to assure them they haven't been charged for it. Samples should never be charged for, even if their barcode is identical to something we normally charge for. If the warehouse is unable to ship the sample, the customer should not receive a missing-item apology or a separate shipment, and the record saying that customer has had that sample already should be decremented. If the warehouse can't ship anything except the sample, the entire orders should be delayed/cancelled, never shipping the sample alone. If a customer ordered three of an item and was assigned one sample item with the same barcode but the warehouse only had three items with that barcode in stock, something sensible should happen. One key type of 'sample' is first-time-customer gifts; internal documentation should explain that if the first order a customer places is on 14-day delivery and their second order is on faster delivery and arrives first, the first-order gift will be in the second order to arrive but that's expected because it's assigned at checkout. If the first-order-checked-out is cancelled, either by the customer or the warehouse, the new-customer gift should be added to the next order they check out. Some customers will want to opt out of free samples, those who do should not be assigned any samples. But the free sample system is also used by customer services to give out token apology gifts to customers whose orders have had problems, customers who've been promised a gift should receive it even if they've opted out of free samples.
No reasonable person can design such a system upfront, because things like 'opt-out mechanism sometimes shouldn't opt you out' and 'more than one definition of a customer's first order' do not occur to reasonable people.
I would hope that a large successful online retailer got that way by factoring their implementation so that many aspects can be dealt with mostly as a configuration matter. This is mistaking quantity for difficulty. First of all, separate the domains fulfillment doesn't care about pricing, but they do care about grouping items in a shipment so they should already have had grouping rules, so apply the 'do not ship this item alone' rule, etc. The other pattern to apply repeatedly here is to separate the decision-making from effecting a change, i.e. separation of policy from mechanism. So you can have a library of mechanisms (e.g. add item to order at checkout) vs the policies which decide who, which item, and what to charge. If you don't conflate all these separate concerns as a single 'thing' to begin with, then none of the individual things is complicated, just has to be the right things in the right places.
This thought processes does use some knowledge of online retail but not really that much. It's mostly patterns of system decomposition and good engineering.
Edit: the point of the article itself stands, if the codebase is in no shape to have these free samples built as I described then my input is useless, other than to consider working toward that architectural goal.
Hmm, sounds familiar...
Bingo knows everyone's name-o
Papaya & MBS generate session tokens
Wingman checks if users are ready to take it to the next level
Galactus, the all-knowing aggregator, demands a time range stretching to the end of the universe
EKS is deprecated, Omega Star still doesn't support ISO timestamps
https://www.youtube.com/watch?v=y8OnoxKotPQ
In 30 years in software dev, I am yet to see any significant, detailed and consistent effort to be extended into design and architecture. Most architects do not design, do not architect.
Senior devs design and architect and then take their design to the architects for *feedback and approvals*.
These senior devs make designs for features and only account for code and systems they've been exposed to.
With an average employment term of 2 years most are exposed to a small cut of the system, which affects the depth and correctness of their design.
And architects mostly approve, sometimes I think without even reading the docs.
At most, you can expect the architects to give generic advice and throw a few buzzwords.
At large, they feel comfortable and secure in their positions and mostly don't give a shit!
https://www.goodreads.com/en/book/show/39996759-a-philosophy...
Video overview at:
https://www.youtube.com/watch?v=bmSAYlu0NcY
I've been thinking about this a lot. 2~3 years is a long time, long enough to have a pretty good grasp on what a code maintained by 50~100 does in pretty concrete terms, come up with decent improvement ideas, and see at least one or two structural ideas hit production.
If the person then stays 1 or 2 more years they get a chance to further refine, but usually will be moved up the ladder Peter Principle style. If they get a chance to lead these architecture changes that company has a chance to be on a decent path technally speaking.
I'm totally with you on the gist of it: architects will usually be a central switch arranging these ideas coming from more knowledgeable places. In the best terms I see their role as guaranteeing consistency and making sure teams don't impede each other's designs.
But this is exactly the type of generic software design advice the article warns us about! And it mostly results in all all the bad software practices we as users know and love remaining unchanged (consistently "bad" is better than being good at least in some areas!)
What you describe just sounds "inconsistent AND bad".
Consistency enables velocity. If there is consistency, devs can start to make assumptions. "Auth is here, database is there, this is how we handle ABC". Possible problems show up in reviews by being different to expectation. "Hey, where's XYZ?", "Why are you querying the database in the constructor?"
Onboarding between teams becomes a lot easier, ramp up time is smaller.
Without consistency, you end up with lots of small pockets of behavior that cause downstream problems for the org as a whole.
Every team needs extra staff to handle load peaks, resulting in a lot of idle devs.
Senior devs can't properly guess where the problematic parts of fixes or features would be. They don't need to know the details, just where things will be _difficult_.
Every feature requires coordination between the teams, with queuing and prioritizing until local staff become available.
Finally, consistency allows classes of bugs to be fixed once. Fix it once and migrate everyone to the new style.
I think adherence to “consistency is more important than ‘good design’” naturally leads to boiling the ocean refactoring and/or rewrites, which are far riskier endeavors with lower success rates than iterative refactoring of a working system over time.
migrate the rest of the codebase!
Then everyone benefits from the discovery.
If that's difficult, write or find tooling to make that possible.
It's in the "if it hurts, do it more often" school of software dev.
https://martinfowler.com/bliki/FrequencyReducesDifficulty.ht...
There’s absolutely exceptions and nuances. But I think when weighing trade-offs, program makers by and large deeply under-weigh being consistent.
software that has "good" and "bad" parts in unpredictable
Software that has only "bad" parts is also very unpredictable.
(Unless "bad" means something else than "bad", it's hard to keep up with the lingo)
its true and false at the same time, it depends
here I can bring example: you have maintaining production system that has been run for years
there is flaw in some parts of codebase that is probably ignored either because
1. bad implementation/hacky way
2. the system outgrow the implementation
so you try to "fix" it but suddenly other internal tools stops working, customer contact the support because it change the behaviour on their end, some CI randomly fails etc
software isn't exist in a vacuum, complex interaction sometimes prevent "good" code to exist because that just reality
I don't like it either but this is just what it is
But yes, the map is not the territory, and giving directions is not the same as walking the trail. The actual implementation can deviate from the plan drafted at the beginning of the project. A good explanation is found in Naur's Theory of Programming, where he says the true knowledge of the system is inside the head of the engineers that worked on it. And that knowledge is not easily transferrable.
I think this should also apply to people who come up with or choose the software development methodology for a project. Scrum masters just don't have the same skin in the game that lead engineers do.
Modules with different requirements should not have single consistent codebase. Testing strategy, application architecture, even naming should be different across different modules.
On the other hand, there are Real Programmers [0] who will happily optimize the already-fast initializer, balk at changing business logic, and write code that, while optimal in some senses, is unnecessarily difficult for a newcomer (even an expert engineer) to understand. These systems have plenty of detail and are difficult to change, but the complexity is non-essential. This is not good engineering.
It's important to resist both extremes. Decision makers ultimately need both intimate knowledge of the details and the broader knowledge to put those details in context.
0. http://www.catb.org/jargon/html/story-of-mel.html
Structural Engineering (generally construction engineering) does work like that. Following the analogy, the engineers draw; they don't lay bricks. But, all the best engineers have probably been site supervisors at some point and have watched brick being layed, and spoken to the layers of bricks, etc. Construction methods change, but they don't change as quickly as software engineering methods. There is also a very material and applicable "reality" constraint. Most struct's knowledge/heuristics remains valid over long periods of time. The software engineers' body of knowledge can change 52 times in a year. To completely stretch the analogy - the site conditions for construction engineering are better known than the site conditions for a large software project. In the latter case the site itself can be adjusted more easily, and more materially, by the engineering itself i.e. the ground can move under your feet. Site conditioning on steroids!
Ultimately, that's why I agree fully with the piece. Generic advise may be helpful, but it always applies to some generic site conditions that are less relevant in practice.
I don't view that as a failure of abstraction as a design principle as much as it is a pitfall of using the wrong abstraction. Using the right abstraction requires on the ground knowledge, and if nobody communicates that up the chain, well, you get the tree swing cartoon.
[1] https://en.wikipedia.org/wiki/Tree_swing_cartoon
The problem is that doing it like that is much too expensive and too slow for most businesses.
"Business" runs the same calculations. I'd posit that, as a practical matter, most businesses don't want "good" software; they want "good enough" software.
A lot of this is because while a 'good' business is waiting for the 'good' software to be written, some crappy business has already written the crappy software and sold it to all the customers you were depending on. In general customers are very bad at knowing the difference between good and bad software and typically buy what looks flashy or the sales people bribe them the most for.
Nah, those changes are only in the surface, at the most shallow level.
There's always new techniques, materials and tools in structural engineering as well.
Foundations take a lifetime to change.
Very strongly disagree.
There are limitless methods of solving problems with software (due to very few physical constraints) and there are an enormous number of different measures of whether it's "good" or "bad".
It's both the blessing and curse of software.
There are too many decisions, technical details, and active changes to have someone come in and give direction from on high at intervals.
Maybe at the beginning it could make sense sort of, but projects have to evolve and more often than not discover something important early on in the implementation or when adding "easy" features, and if someone is good at doing software design then you may need them even more at that point. But they may easily be detrimental if they are not closely involved and following the rest of the project details.
When you have done this many times you absolutely can design a large application without touching the code. This is part planning and risk analysis experience and part architecture experience. You absolutely need a lot of experience creating large applications multiple times and going through that organizational grind but prior experience in management and writing high level plans is extremely helpful.
When it comes to extending an existing application it really comes down to how well the base application was planned to begin with. In most cases the base application developers have no idea, because they either outsourced the planning to some external artifact or simply pushed through it one line at a time and never looked back. Either way the person writing the extension will be more concerned with the corresponding service data and accessibility than conformance to the base application code if it is not well documented and not well tested in a test automation scheme.
Imagine if you worked for an online retailer like Amazon, and you were assigned to architect a change so you can add free sample items into customers' orders. Take a moment to think about how you'd architect such a system, and what requirements you'd anticipate fulfilling. In the next paragraph, I'll tell you what the requirements are. Or you can skip the next paragraph, the size of which should tell you the requirements are more complex than they seem.
The samples must be items in the basket, so the warehouse knows to pick them. They must be added at the moment of checkout, because that's when the order contents and weight can change. Often a customer should receive a sample only once, even if they check out multiple orders - so a record should be kept of which customers have already been allocated a given sample. It should be possible to assign a customer the same sample multiple times, in which case they should receive it once per order until they've received the assigned number. Some samples go out of stock regularly, so the sample items should not be visible to the customer when they view their order on the website, but if shipped it should appear on their receipt to assure them they haven't been charged for it. Samples should never be charged for, even if their barcode is identical to something we normally charge for. If the warehouse is unable to ship the sample, the customer should not receive a missing-item apology or a separate shipment, and the record saying that customer has had that sample already should be decremented. If the warehouse can't ship anything except the sample, the entire orders should be delayed/cancelled, never shipping the sample alone. If a customer ordered three of an item and was assigned one sample item with the same barcode but the warehouse only had three items with that barcode in stock, something sensible should happen. One key type of 'sample' is first-time-customer gifts; internal documentation should explain that if the first order a customer places is on 14-day delivery and their second order is on faster delivery and arrives first, the first-order gift will be in the second order to arrive but that's expected because it's assigned at checkout. If the first-order-checked-out is cancelled, either by the customer or the warehouse, the new-customer gift should be added to the next order they check out. Some customers will want to opt out of free samples, those who do should not be assigned any samples. But the free sample system is also used by customer services to give out token apology gifts to customers whose orders have had problems, customers who've been promised a gift should receive it even if they've opted out of free samples.
No reasonable person can design such a system upfront, because things like 'opt-out mechanism sometimes shouldn't opt you out' and 'more than one definition of a customer's first order' do not occur to reasonable people.
This thought processes does use some knowledge of online retail but not really that much. It's mostly patterns of system decomposition and good engineering.
Edit: the point of the article itself stands, if the codebase is in no shape to have these free samples built as I described then my input is useless, other than to consider working toward that architectural goal.