Avoiding the Perfect Design
One of the peculiar ironies that I’ve discovered by watching the way a lot of different software shops work is that the most intense moments of exuberance about software seem to occur in places where software development happens at glacial speeds. If you walk into an agile shop or a startup or some kind of dink-and-dunk place that bangs out little CRUD apps, you’ll hear things like, “hey, a user said she thought it’d be cool if she could search her order history by purchase type, so let’s throw that in and see how it goes.” If it goes insanely well, there may be celebrations and congratulations and even bonuses changing hands which, to be sure, makes people happy. But their happiness is Mercury next to the blazing Sun of an ivory tower architect describing what a system SHALL do.
“There will be an enterprise service bus. That almost goes without saying. The presentation tier and the business tier will be entirely independent of one another, and literally any sort of pluggable module that you dream up as a client can communicate with any sort of rules engine embedded within the business tier. Neither one will EVER know about the other’s existence. The presentation layer collaborators are like Schrodinger and the decision engines are like the cat!
And the clients. Oh yes, there will be clients. The main presentation tier client is a mobile staging environment that will be consumed by Android, iOS, Windows Phone, Blackberry, and even some modified Motorolla walkie-talkies. On top of the mobile staging environment will be a service adapter that makes it so that clients don’t need to worry about whether they’re using SOAP or REST or whatever comes next. All of those implementations will hide behind the interface. And that’s just the mobile space. There are more layers and subtleties in the browser and desktop spaces, since both of those types of clients may be SPAs, other thick clients, thin clients, or just leaf nodes.
Wait, wait, wait, I’m not finished. I haven’t even told you about the persistence factories yet and my method for getting around the CAP theorem. The performance will be sublime. We’re talking picoseconds. We’re going to be using dynamically generated linear programming algorithms to load balance the vertical requests among the tiers, and we’re going to take a page out of the quantum computing book to introduce a new kind of three state boolean… oh, sorry, you had a question?”
“Uh, yeah. Why? I mean, who is going to use this thing and what do they want with it?”
“Everyone. For everything. Forever.”
You back out slowly as the gleam in his eye turns slightly worrisome and he starts talking about the five year plan that involves this thing, let’s call it HAL, achieving sentience, bringing humankind to the cusp of the singularity, and uploading the consciousnesses of all network and enterprise architects.
Like I said, the Sun to your Mercury. Has your puny startup ever passed the Turing Test? Well, his system has… as spec’ed out in a document management system with 8,430 pages of design documents and a Visio diagram that’s rumored to have similar effects to the Ark of the Covenant. And that, my friends, is why I think that a failing ATDD scenario should be the absolute first thing anyone who says, “I want to get into programming” learns to do.
Now to justify that whiplash-inducing segue. I wrote a book about unit testing in which I counseled complete initiates to automated testing to forgo TDD and settle for understanding the mechanics of automated tests and test runners first before making the leap. I stand by that advice, but I do so because I think that there is a subtle flaw to the way that most people currently get started down the programming path.
I was watching a Pluralsight course about NUnit to brush up on their latest and greatest assertion semantics, and the examples were really well done. In particular, there was a series of assertions oriented around a rudimentary concept of a role playing game with enumerations of weapons, randomization of events, and hit points. This theme exercised concepts like ranges, variance, collection cardinality, etc and it did so in a way that lent itself to an easy mental model. The approach was very much what mine would have been as well (I wouldn’t have come at this with TDD because there’d have been a lot of ‘downtime’ writing the production code as opposed to just showing the assert methods).
Nevertheless, it’s been a while since I’ve watched someone write tests against a pre-baked system when they weren’t characterization tests in a legacy rescue, and the experience was sort of jarring. I couldn’t help but think, “I wouldn’t want to write these tests if I were in his position — why bother when the code is already done?” Weird as it sounds from a big advocate of testing, writing tests after you’ve completed your implementation feels like a serious case of “going through the motions” in the same way that developers fill out random “SDLC” artifacts for no other purpose than to get PMPs to leave them alone.
And that’s where the connection to the singularity architect comes in. One of the really nice, but subtle perks of the TDD (especially ATDD) approach is that it forces you to define exit criteria before you start doing things. For instance, “I know I’ll be done with this development effort when my user can search her order history by purchase type.” Awesome — you’re well on your way because you’ve (presumably) agreed with stakeholders ahead of time when you can stop coding and declare victory. The next thing is to prove it, and you can approach this in the same way that you might approach fixing a leaking pipe under your sink. Turn the water on, observe the leak, turn the water off, fix the leak, turn the water back on, observe that there is no leak. Done.
In the case of the search, you write a client call to your web service that supplies a “purchase type” parameter and you say that you’re done when you get a known result set back, instead of the current error message: “I do not understand this ‘purchase type’ nonsense you’ve sent — 400 for you!” Then you scurry off to code, and you just keep going until that test that you’ve written turns green and all of the other ones stay green. There. Done, and you can prove it. Ship it.
Our poor architect never knows when he’s done (and we know he’ll never be done). The origin of this Sisyphean struggle started with hobby programming or a CS degree or something. It started with unbounded goals and the kinds of open-ended tasks that allow hobbyists to grow and students to excel. Alright, you’ve got the A, but try to play with it. See if you can make it faster. Try adding features to it. Extra credit! Sky’s the limit! At some point, a cultural norm emerges that says it’s more about the journey than the destination. And then you don’t rise through the ranks by automating for the sake of solving people’s problems but rather by building ever-more impressive juggernauts, leveraging the latest frameworks, instrumented with the most analytics, and optimized to run in O(Planck Time).
I really would like to see initiates to the industry learn to set achievable (but slightly uncomfortable) goals with a notion of value and then reach them. Set a beneficial goal, reach it, rinse, repeat. The goal could be “I want to learn Ruby and I’ll consider a utility that sorts picture files to be a good first step.” You’re adding to your skill set as a developer and you have an exit criteria It could be something for a personal project, a pro-bono client, or for pay. But tie it back to an outcome and assess whether that outcome is worthwhile. This approach will prevent you from shaving microseconds off of an app that runs overnight on a headless server and it will prevent you from introducing random complexity and dependency to an app because you wanted to learn SnazzyButPointless.js. True, the approach will stop you from ever delighting in design documents that promise the birth of true artificial intelligence, but it will also prevent you from experiencing the dejection when you realize it ain’t ever gonna happen.
Man your writing skills are awesome.
Your posts express exactly what I feel.
Like the “SnazzyButPointless.js” reference ^^
Thanks! I’m glad you enjoyed. 🙂
I think the “boring” angle is a really important one. Bored developers will absolutely invent problems to solve if the actual problems presented are not interesting or challenging. I think figuring out a way to align developers’ desire to problem solve, with actual, valuable problems to be solved is an underrated but critical prerequisite for success.
Whenever people start project planning like in your example, I like to jokingly point out that they sound like the stoners in the movie Half Baked when they are planning their grocery run: What do you guys want ? Get some… sour cream and onion chips… with some dip, man. Some beef jerky. Some peanut butter. Get some Haagen Dazs ice cream bars. A whole lot. Make sure chocolate. Gotta have chocolate, man. Some popcorn. Bread. Popcorn. Graham crackers. Graham crackers with the marshmallows, the little marshmallows. And little chocolate bars. We’ll make some smores, man. Yeah, that’s what l… Read more »
I’m definitely going to be picturing this the next time I hear someone talk like this. I think it’ll be hard for me not to wait for him to finish and then just say, “And Funyuns… yeah!”
Fantastic article, I’ve found myself running down a similar rabbit hole, where “expandable” and “adaptable” has meant not producing any outcome. One extra set of functionality comes with hours of inventing, tons of complexity and very little result. I recently had a chat with a respected architect whose been in the game probably longer than I’ve been on this earth and he answered a design question I had with: “What ever will keep the CLIENT happy and get the job done in the time allocated”. My eyes have been opened 🙂
Yes, exactly. Keeping what the stakeholders want in mind is really important, I think. Otherwise, you just wind up building whatever you think is the most interesting or future-proof or whatever thing to build.
When can I get that awesome Motorola Walkie-Talkie app?
P.S. Spelling correction: I think you meant: “But tie it back to an outcome and asses*”
I like tying things to my ass, but I don’t think that is what you meant. 🙂
HAHA. Correcting my own correction:
P.S. Spelling correction: I think you meant: “But tie it back to an outcome and assess*”
Missing final ‘S’
Thanks for the heads up, and fixed. Gave me a chuckle. The weird thing is, I could have sworn I remembered seeing that mistake was I was reading over the post, chuckling, and fixing it. I guess maybe I just chuckled and accidentally left it in.
I guess the more general message would be ‘Avoid Perfect Anything’. It’s always about finding the optimal balance between cost and benefit. Unless what you’re doing is Art (and software development isn’t).
Absolutely. Design, code, whatever. There’s always a tendency to want to perfect instead of getting something usable in the hands of those wanting to use it.
Our poor architect never knows when he’s done Maybe the fictitious architect in your facetious example. A true architect is “done”, blazingly fast. On any given project that is measured in around a year of time to delivery an experienced architect can produce an architecture design and project design (including resource/employee allocation for the entire project) with an exact delivery date. For a project of this size all of this can be done in 2-3 weeks. A timespan that is trivial compared 3, 5, 9 people or more all working on a project. In general the architecture cost of a… Read more »
What does the architect’s up front deliverable look like? In other words, it sounds from your description like the architect spends 2-3 weeks and comping up with “architecture/project design” and a staffing model. I’m wondering what that architecture/project design looks like, and what sort of delivery model this describes (some flavor of agile, iterative, phased, development, waterfall, etc).
the system design using volatility based decomposition of reusable software subsystems. generally a system will be composed over 12 – 24 subsystems, a design exceeding 2 dozen subsystems is likely either a functional design (no reusability, bad modularity) or a gigantic system that would be better off split into multiple separate systems. a subsystem would be a client application, a business logic sequencer/orchestrator, an engine/calculation, resource access, physical resource (3rd party web service, database), or a utility (message bus, logging). those subsystems are ordered in least reusable to most reusable. the system design document would show call chains of how… Read more »
Hello All friends if you are geting bored then i have a very nice chat room where you can find pakistani girls, Indian Girls, Uae Girls, Uk Girls, Adult Girls, Dating Girls, Canada Girls, Islambad Grisl, Multan Girls Chat Room, If You Wana Join This Chat Room Then You Do Not Need Any Typ Of Registration Just Enter Your Nick And Enjoy A Pakistani Chat Rooms
…it forces you to define exit criteria before you start doing things.
Our poor architect never knows when he is done…
Very precise point. Thanks for the instructive article.
Thanks for the feedback, and glad you liked!
“There will be an enterprise service bus. That almost goes without saying.”
Look like I’m not the only one who run for his life when he hears the word “bus” … In my experience it always end in disaster (or never end, which is another kind of disaster).