More on this book
Community
Kindle Notes & Highlights
Care About Your Craft We feel that there is no point in developing software unless you care about doing it well.
“Kaizen” is a Japanese term that captures the concept of continuously making many small improvements.
As Martin Fowler says, “you can change your organization or change your organization.”[3]
When disorder increases in software, we call it “software rot.” Some folks might call it by the more optimistic term, “technical debt,” with the implied notion that they’ll pay it back someday. They probably won’t.
Don’t leave “broken windows’’ (bad designs, wrong decisions, or poor code) unrepaired. Fix each one as soon as it is discovered. If there is insufficient time to fix it properly, then board it up. Perhaps you can comment out the offending code, or display a “Not Implemented” message, or substitute dummy data instead. Take some action to prevent further damage and to show that you’re on top of the situation.
Work out what you can reasonably ask for. Develop it well. Once you’ve got it, show people, and let them marvel. Then say “of course, it would be better if we added…’’ Pretend it’s not important. Sit back and wait for them to start asking you to add the functionality you originally wanted. People find it easier to join an ongoing success. Show them a glimpse of the future and you’ll get them to rally around.
Learn at least one new language every year Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut.
Read a technical book each month
It’s not just what you’ve got, but also how you package it. Having the best ideas, the finest code, or the most pragmatic thinking is ultimately sterile unless you can communicate with other people. A good idea is an orphan without effective communication.
English is Just Another Programming Language
Probably the most difficult part of the more formal styles of communication used in business is working out exactly what it is you want to say.
Plan what you want to say. Write an outline. Then ask yourself, “Does this communicate what I want to express to my audience in a way that works for them?” Refine it until it does.
Make what you’re saying relevant in time, as well as in content. Sometimes all it takes is the simple question, “Is this a good time to talk about…?’’
Too many developers (and their managers) concentrate solely on content when producing written documents. We think this is a mistake. Any chef (or watcher of the Food Network) will tell you that you can slave in the kitchen for hours only to ruin your efforts with poor presentation.
There’s one technique that you must use if you want people to listen to you: listen to them.
Always respond to emails and voicemails, even if the response is simply “I’ll get back to you later.’’
Build Documentation In, Don’t Bolt It On It’s easy to produce good-looking documentation from the comments in source code, and we recommend adding comments to modules and exported functions to give other developers a leg up when they come to use it.
So restrict your non-API commenting to discussing why something is done, its purpose and its goal. The code already shows how it is done, so commenting on this is redundant—and is a violation of the DRY principle.
Keep quoting to a minimum. No one likes to receive back their own 100-line email with “I agree” tacked on.
The Mythical Man-Month: Essays on Software Engineering [Bro96] and Peopleware: Productive Projects and Teams [DL13]. Make it a point to try to read these over the next 18 months. In addition, Dinosaur Brains: Dealing with All Those Impossible People at Work [BR89] discusses the emotional baggage we all bring to the work environment.
Also think about languages and programming paradigms (OO, FP, Reactive, and so on). Do any have either big positives or big negatives when it comes to helping you write ETC code? Do any have both?
maintenance is not a discrete activity, but a routine part of the entire development process.
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
DRY is about the duplication of knowledge, of intent. It’s about expressing the same thing in two different places, possibly in two totally different ways.
Our data structures represent knowledge, and they can fall afoul of the DRY principle.
This example also illustrates an important issue: whenever a module exposes a data structure, you’re coupling all the code that uses that structure to the implementation of that module. Where possible, always use accessor functions to read and write the attributes of objects. It will make it easier to add functionality in the future.
Meyer’s Uniform Access principle, described in Object-Oriented Software Construction [Mey97]
All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.
I think this means you dont need to know if i cached it. But it could mean a lot more. Not sure if its mainly about syntax or also semantics
There’s another option, and one we often prefer. Rather than writing code that represents external data in a fixed structure (an instance of a struct or class, for example), just stick it into a key/value data structure (your language might call it a map, hash, dictionary, or even object). On its own this is risky: you lose a lot of the security of knowing just what data you’re working with. So we recommend adding a second layer to this solution: a simple table-driven validation suite that verifies that the map you’ve created contains at least the data you need, in the format you need it. Your
...more
However, at the module level, the problem is more insidious. Commonly needed functionality or data that doesn’t fall into an obvious area of responsibility can get implemented many times over.
Auto suggestions and search by type address this. Being intentional about the contract and programmatically expose it.
“Orthogonality’’ is a term borrowed from geometry. Two lines are orthogonal if they meet at right angles, such as the axes on a graph. In vector terms, the two lines are independent.
(what Yourdon and Constantine call cohesion in Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design [YC79]).
You get two major benefits if you write orthogonal systems: increased productivity and reduced risk.
An orthogonal approach also promotes reuse. If components have specific, well-defined responsibilities, they can be combined with new components in ways that were not envisioned by their original implementors. The more loosely coupled your systems, the easier they are to reconfigure and reengineer.
An orthogonal system will probably be better tested, because it will be easier to design and run tests on its components.
Don’t rely on the properties of things you can’t control.
The Enterprise Java Beans (EJB) system is an interesting example of orthogonality. In most transaction-oriented systems, the application code has to delineate the start and end of each transaction. With EJB, this information is expressed declaratively as annotations, outside the methods that do the work. The same application code can run in different EJB transaction environments with no change.
Avoid global data Every time your code references global data, it ties itself into the other components that share that data.
Perhaps surprisingly, orthogonality also applies to documentation. The axes are content and presentation. With truly orthogonal documentation, you should be able to change the appearance dramatically without changing the content. Word processors provide style sheets and macros that help. We personally prefer using a markup system such as Markdown: when writing we focus only on the content, and leave the presentation to whichever tool we use to render it.[17]
C++ supports multiple inheritance, and Java allows a class to implement multiple interfaces. Ruby has mixins. What impact does using these facilities have on orthogonality? Is there a difference in impact between using multiple inheritance and multiple interfaces? Is there a difference between using delegation and using inheritance?
What are the differences in orthogonality between object-oriented and functional languages? Are these differences inherent in the languages themselves, or just in the way people use them?
While many people try to keep their code flexible, you also need to think about maintaining flexibility in the areas of architecture, deployment, and vendor integration.
Tracer code is not disposable: you write it for keeps. It contains all the error checking, structuring, documentation, and self-checking that any piece of production code has. It simply is not fully functional. However, once you have achieved an end-to-end connection among the components of your system, you can check how close to the target you are, adjusting if necessary. Once you’re on target, adding functionality is easy.
But if you find yourself in an environment where you cannot give up the details, then you need to ask yourself if you are really building a prototype at all. Perhaps a tracer bullet style of development would be more appropriate in this case (see Topic 12, Tracer Bullets).