I've always found the discussions around software design some of the most intimidating aspects of programming. With the best of intentions, I still flinch a little when I hear the phrase design pattern. I think one of the reasons for all the fervent dogma around this aspect of software design is how one size doesn't fit all, and even with all these so-called best practices at the end of the day it's a human problem. Albeit one we try to act like we can definitely solve with methodologies. The way you structure a project in your mind, while potentially being enormously successful in your own practice, might not be anywhere near the most productive way for somebody else.
Yay for individualism! But this can be anathema in an office, where projects are precisely timed, judged and turned into lots of whizzy graphs to show off at various and endless important meetings. We also want our efforts to be reproducible, and the overall processes to be largely understood so we can effectively estimate against it. Stop crushing my free spirit, The Man! Boo!
But wait, it's not all doom and one-day agile development workshop coaches. We do need a common shared context of information for communicating with other developers. One of the trickiest parts of getting started in any specialised field, in my opinion, is growing the knowledge and confidence to articulate your thoughts. When I started with pair programming at a coding bootcamp, I found frustration easily crept in between the gaps of knowledge and communication: many of us knew where to start solving a problem, but found it extremely difficult with how to discuss it. I still regularly find myself resorting to muttering about 'that thingy' or doing 'some stuff' with the 'whatsit'.
Developing this internal monologue - in my mind, reader, I am Jane Austen and my code is a novel - is as useful for your own personal code development as it is for working in a team. For instance, I recently realised my first few steps in design as a software developer could be neatly summed up by this meme:
I knew where I was (two circles) and I knew where I wanted to end up (the owl), but I'd be royally danged if I could think of literally anything that could help get me to move between steps 1 and 2. So I'd just sit there, trapped in my own analysis paralysis until someone else came along and helped push me along the path. I was often mentally blocked in a world where everything was either a thingy, a whatsit or a finished owl.
TDD - wait! come back! I'm not going to try and sell you an eBook or offer any agile coaching services! - is often prescribed as a tidy cure to this ailment. As a fan of the classics, I've recently started reading an endearingly dog-eared copy of Kent Beck's Test Driven Development by Example and it's done a really good job of helping me actually break through this barrier from time to time.
There's plenty of sage advice within the book (I'd say it's definitely worth picking up a copy), but the key benefit I've been able to extract from it, with the way my my works, is in making my feedback loops shorter - reaching for the little change and starting to build the mountain of my application from many, many molehills.
Beck frequently reminds us not to jump ahead too far - I think the book helps demonstrate a framework for thinking more than anything else, and frequent reminders to maintain an active todo list is a useful guides for practising pacing and scope. He also reminds us there's no point in being too dogmatic about everything: if there's an obvious implementation, then just write it. Keeping a todo list also encourages me to doodle more, which occasionally helps me figure out my next steps. And that's super cool!
Here's the exact moment I realised, on a slightly grim-coloured, off-brand Post-it® note, how HTTP server responses are kind of numbered in the order you might process the request, which blew my mind:
Once we start with a failing test, and then make it run, Beck introduces three strategies to start progressing towards making our code work the way we want it to:
- Fake it: Return a constant and gradually replace constants with variables until you have a fully-baked implementation.
- Use Obvious Implementation: Just type in the real implementation.
- Triangulation: The most conservative strategy, where we only start to generalise our code when there are two or more examples. This helps to eke towards the right path if you're getting lost on your design, which I frequently am.
Right now, I like to consciously think about which one of these strategies I'm employing and really actively think about it.
Something else I really liked about the book is that it helped me think more about the rhythm of development - speeding up and slowing down as appropriate. I've always been accidentally partial to a bit of speed archaeology, which helps me blaze ahead in the first few hours but quickly creates more problems than it solves.
The other thing I like about working like this - short, intentional steps - is how it's an incremental approach. Trying to memorise two dozen complicated design patterns and the exact right time of when to use them is both exhausting and overly intimidating. So most of the time I try and guide myself with a far simpler set of strategies, and then I can reach for a design pattern when I get a sense that my code is missing a certain... something.
Finally, one of the biggest challenges about your approach to design is that it doesn't sit still. These things ebb and flow regardless of matter how many Gang of Four patterns you've had tattooed across your back. When I started developing in React I couldn't even get half-way through Googling my problem before someone was telling me about a presentational or container component, and now Dan Abramov has updated his blog post on the pattern to include the following caveat:
I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component. Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.
Partially related: reading Dan Abramov's blog is a wonderfully humbling experience which brings frequent joy into my life.
Trying to separate out my sense of intimidation from design with some real-life approaches is what worked for me, but the frustrating beauty of it all is that it might not work for anyone else. If it feels painfully new to you, like it does with me, I'd recommend just trying a bunch of stuff out. When you start to find those methods which resonate with you, eventually you realise you're actually drawing the owl.
Has this post been useful for you? I'd really appreciate any comments and feedback on whether there's anything that could be made clearer or explained better.
Top comments (0)