All of this stuff has been about dealing with abstraction. Seriously, think about what you spend most of your time doing when you're programming. For me, I think it breaks down something like this:
- 50%: Figuring out why a framework/library/control/object isn't doing what I wanted it to do
- 30%: Deciding where to put things and what to name them
- 20%: Actually writing business logic/algorithms
So, yeah, those numbers are completely made up, but they feel right... And look at how much time is spent just deciding where things should go! Seriously, that's what "software engineering" really is. What should I name this? Where should I put this logic? What concepts should I express? How should things communicate with each other? Is it OK for this thing to know about some other thing?
This is abstraction. You could just write everything in one long file, top to bottom, probably in C. We've all been there, to some degree, and it's a nightmare. So we slice things up and try to hide details. We try to massage the code into some form that makes it easier to understand, more intuitive. Abstraction is our only way to deal with complexity.
But abstraction itself is a form of complexity! We are literally fighting complexity with complexity.
I think this is where some of the art of programming comes into play. One of the elements of beautiful code is how it's able to leverage complexity but appear simple.
I think this is where some of the art of programming comes into play. One of the elements of beautiful code is how it's able to leverage complexity but appear simple.
All of our design patterns, principles, and heuristics (from Clean Code) can largely be viewed as suggestions for "where to put things and what to name them" that have worked for people in the past. It's more like an aural tradition passed from generation to generation than it is like the strict rules of Civil Engineering. This doesn't make any of these patterns less important, but it does help explain why good design is such a difficult job, even with all these patterns.
In this series of posts I've blabbed on about a number of different design topics, each time almost coming to some kind of conclusion, but never very definitively. That's because this has been sort of a public thought experiment on my part. I'm just broadcasting some of the topics that I have been curious about recently. And I think where I would like to leave this is with one final look at the complexity of abstraction.
In this series of posts I've blabbed on about a number of different design topics, each time almost coming to some kind of conclusion, but never very definitively. That's because this has been sort of a public thought experiment on my part. I'm just broadcasting some of the topics that I have been curious about recently. And I think where I would like to leave this is with one final look at the complexity of abstraction.
Abstraction, by definition, is about hiding details. And anyone who has ever done any nontrivial work with SQL Server (or any database) will understand how this hiding of details can be a double edged sword. If you had to understand everything in it's full detail, you could never make it work. But sometimes those hidden details stick out in weird ways, causing things to not work as you were expecting. The abstraction has failed you. But this is inevitable when hiding details, at some point, one of those details with surprise you.
Indirection and abstraction aren't exactly the same thing, but they feel very related somehow, and I think Zed Shaw would forgive me for thinking that. Indirection, just like abstraction, is a form of complexity. And just like abstraction, it can be incredibly useful complexity. I assert that every time you wrap an implementation with an interface, whether it's a Header Interface or an Inverted Interface, you have introduced indirection.
In Bob Martin's Dependency Inversion Principle article, he lays out 3 criteria of bad design:
I think there is clearly a 4th criteria which is: Complexity: Code is overly complicated making it difficult to understand and work with. Every move in the direction of the first three criteria, is a move away from the fourth. Our challenge then, indeed our art, is to find the sweet spot between these competing extremes.
And so I ask you, is it a beautiful code type of thing:
Indirection and abstraction aren't exactly the same thing, but they feel very related somehow, and I think Zed Shaw would forgive me for thinking that. Indirection, just like abstraction, is a form of complexity. And just like abstraction, it can be incredibly useful complexity. I assert that every time you wrap an implementation with an interface, whether it's a Header Interface or an Inverted Interface, you have introduced indirection.
In Bob Martin's Dependency Inversion Principle article, he lays out 3 criteria of bad design:
- Rigidity: Code is hard to change because every change affects too many parts of the system
- Fragility: When you make a change, unexpected parts of the system break
- Immobility: Code is difficult to reuse because it cannot be disentangled from the current application
I think there is clearly a 4th criteria which is: Complexity: Code is overly complicated making it difficult to understand and work with. Every move in the direction of the first three criteria, is a move away from the fourth. Our challenge then, indeed our art, is to find the sweet spot between these competing extremes.
And so I ask you, is it a beautiful code type of thing:
- To wrap every concept behind abstract objects (objects or data structures)?
- To represent every behavior as a method of some stateful thing (service layers or oop)?
- To hide every class behind an indirection in the form of an interface (header interfaces or inverted interfaces)?
- To make every class reusable and plugable (DIP: loose or leaky)?
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.