I have read this book lately, after lots of recommendations online and in dev.to as well, i'll try to summarize it with it best ideas and give my own thoughts on it - mostly to do another 'review' of the book for myself.
Book tells lessons learned by Max Kanat-Alexander, architect of the Bugzilla project, about his refactoring issues, rewriting and management of the project.
It tries to give ideas about the 'right way' of software design, by introducing 'laws' and 'rules'. It's actually tries to give 'philosophical notions' to programming and software development, mostly with 'easy' (to grasp) and known notions.
I think - if each one of us can have set of 'ideas', 'rules' or guidelines in our minds, (maybe write them down on our offices, conduct 'learning sessions' or practice until they settle) could help us in becoming better software developers. Reading those things in a book, can 'enlighten' us about concepts, making us more aware of them and in practice - write better code. For example: if we embrace simplicity and 'YAGNI' as valuable concepts, we will think on them automatically every-time we write new code. Another example is embracing TDD (which is not mentioned in the book), if we decide to do TDD, this can highly affect the way we code, from the time it takes to the quality. In future, those ideas will lead to better code written.
Books starts with this notion - as one of the main things a developer have to know and practice - how to better understand the problem. Max says that it is also a metric to evaluate programmers - a 'good' programmer knows what he is doing every-time and a 'bad' one does not. Writing code is a mental activity where understanding is everything, if you understand what you do precisely - you will do it better.
I think this is an obvious notion but clearly an eye-opener, it looks very simple and 'known' at start, but, do we really think about 'fully' understanding our task each time before writing code? do we really know what we are doing every-time? what are we trying to achieve? this can give us a new way to think on our (and while doing) software development process.
I think starting with writing a uni-test is a good practice as well, it can help understand what we are trying to achieve, by explaining it via a test.
Further more - doing an 'understanding sessions' while implementing or designing or just in middle of a sprint can be a good practice, stop a side and explain / discuss your task with the other teammates or with team leader, to verify- that you understand what you are trying to do. Adding refactoring missions to our sprint - by just checking old components and re-thinking if they are achieving their objective or not - could be another good practice I should embrace.
The purpose of software is to help people:
Again, an obvious-easy-dummy idea, but - thinking on it every-time we choose tasks for current sprint or designing our program- do we really think if it achieves the purpose of helping someone? would changing and setting our logs in a faster none-relational database can help people? not immediately but it will reduce search time and in future would help out IT people to fix users problems or to track them, and eventually help users.
Thinking on this while planning our feature requests and coding, could help in reducing writing useless code / components and even rejecting future requests from our bosses / pm.
The three flaws:
This is a great concept, noting about three major flaws in software design: as our program grows, pieces of it will be needed to change, rather if it is due to customer demands, bugs or new technologies. If you want the program to keep be useful to people - you have to continually change your system as well. You should expect that you will cope with changes of your software.
Max notes three mistakes programmers usually do, that makes it much harder to cope with change:
a. Write code that isn't needed.
b. Not making code easy to change.
c. Being too generic.
I think the first one is the most important - don't write code before you actually need it ('YAGNI'). This idea has made some thoughts for myself, as there were times when writing new code I have also added unused methods for 'future' use - this is a waste. It's related to writing new methods, classes and more. This simple concept is vital - write just the code you need for the current problem, if you will need to expand it - do it later. Don't over-design your code, it's a waste of time, it's adding complexity and it's impossible to handle 'every possible thing' as demands are always changing.
Good design means your code is needed, thus, writing unused code also leads to a bad design.
This can save us time while programming new modules - and reduce stressful thoughts. Removing code that is not in use is also important - it can reduce misunderstanding of methods and components, which might lead to different design. If you need the code once in future - just go back in your VCS. It is also should be an important ritual in doing Code Reviews -- verify that there is no 'unused code' and 'not needed code'.
The simpler the pieces are, the more easily you can change things in the future. The idea is to make the individual components of your code as simple as possible, and then make sure they stay that way over time. This can be tested as how easily a module is understood by others. The notion assumption is as our software grows - our code base becomes complex and no one can conceive the whole system in his mind all the once.
Simplicity should be in our mind every-time we produce something new.
Incremental development and design:
The flaws mentioned above could be solved using this idea, mainly- we split our tasks into smaller ones and perform small task each time. While we develop incrementally - we also design incrementally, we look back at our design at each step. This approach is executed in Agile development, but not always with the design part. Max notes doing an incremental development - developing features in small iterations, each time developing some piece of it, and incremental design- we build our design incrementally as well. We start with a simple easy design - implement it, then fix the system design to something better. Incremental design is somewhat 'new' and not much 'common', and might need some practice as well, but this is the main addition of Max to the incremental process.
This might be relevant for newcomers and I'm always facing those issues- thinking just to start with something, with a simple design and implement it- could be useful and get things working in start.
Another notion of this is the ORDER of building things, building from "the inside out". build the most important component first, then iterate and build the others by using it or not. Haven't had experience with this notion but it makes sense- if you want to build a calculator, build the 'addition' and 'subtraction' first, then as building them as the base - use the others in top of that. This is an interesting idea that can help in producing better code.
Doing an incremental design is also something that needed to be considered in the agile process - let's add a task of looking back at our design - did we chose the right architecture for this component? do it needs refactoring? and more.
Max notes consistency in context of simplicity, as part of being simple you should also try to be consistent. If we are writing our uni-tests in a specific style of writing - we should keep on doing that. This is helpful as our team mates learn how to use our code in a specific way, being consistent reduces their effort of understanding another part that we have written, as they are familiar with our 'styling' already.
I think this is a simple (yet again) known idea- but if we give our thoughts on it, practice on it - it's a rule we should all embrace.
Software complexity is the notion of how 'complex' is our system - in terms of understanding it and adding code to it. Our software starts small, simple and easy to maintain and over time it gets bigger, complex and difficult to maintain. Defects start to arise and maintainability becomes much more difficult.
From Max perspective, there should be taken big efforts of keeping code simple, and there should be taken active steps in reducing the complexity, by refactoring and making things simple.
Handling complexity - rewriting code:
As a programmer, you will run into complexity - as some parts of your system is too complex and not easy to maintain. If a system needs a whole re-write or a refactoring - it should be done in parallel to writing new features and in small pieces.
In the tension between refactoring vs. rewriting (should we re-write this whole component from scratch or just refactor it), max refactoring and restructuring the existing codebase as the better option,
If this concept interests you, check this.
The last and maybe 2nd-most important part of this book (imo), refers to the only way we can know how our software behaves depends on the degree we have tested it. Or stating it differently - we can only state that our code 'works' if we have tested it. 'Works' is the behavior we expect from our code, if we supply a search() method which returns results - we expect it to behave in a specific way for each query, this must be tested.
I think that in addition to ensuring that our system 'works' as max noted, being hard with tests (covering more and more behaviors of our code with tests) can gives us more functionality then just testing behaviors: it can be our 'documentation' of the code - how to use our code, it can supply cases on how our code being used - if we use a search engine - how queries are answered, what process it makes and more. It can help other developers use the system.
In conclusion- Max notes 'known' ideas and facts about software development, but, reading them in a book you have paid for - and by doing that investing time and concentration on - you are really able to grasp such important ideas. Besides of grasping them - those ideas are highly needed for junior developers and should be practiced and used daily.