More on this book
Community
Kindle Notes & Highlights
Tests can definitely help drive development. But, as with every drive, unless you have a destination in mind, you can end up going in circles.
We like to think of unit testing as testing against contract (see Topic 23, Design by Contract). We want to write test cases that ensure that a given unit honors its contract.
Suppose we have a module A that uses a DataFeed and a LinearRegression. In order, we would test: DataFeed’s contract, in full LinearRegression’s contract, in full A’s contract, which relies on the other contracts but does not directly expose them This style of testing requires you to test subcomponents of a module first. Once the subcomponents have been verified, then the module itself can be tested.
But that ignores the fact that tests are also a way of communicating with other developers, so I now do write tests on code shared with others or that relies on the peculiarities of external dependencies. Andy says I shouldn’t include this sidebar. He worries it will tempt inexperienced developers not to test. Here’s my compromise:
In the first edition’s discussion of code coupling we made a bold and naive statement: “we don’t need to be as paranoid as spies or dissidents.” We were wrong. In fact, you do need to be that paranoid, every day.
Minimize Attack Surface Area Principle of Least Privilege Secure Defaults Encrypt Sensitive Data Maintain Security Updates
Never trust data from an external entity, always sanitize it before passing it on to a database, view rendering, or other processing.
Principle of Least Privilege Another key principle is to use the least amount of privilege for the shortest time you can get away with.
Every program and every privileged user of the system should operate using the least amount of privilege necessary to complete the job.— Jerome Saltzer, Communications of the ACM, 1974.
We believe that things should be named according to the role they play in your code.
Most people we ask would call it fib. Seems reasonable, but remember it will normally be called in the context of its module, so the call would be Fib.fib(n). How about calling it of or nth instead:
Most introductory computer texts will admonish you never to use single letter variables such as i, j, or k.[69] We think they’re wrong. Sort of.
Some languages allow a subset of Unicode in names. Get a sense of what the community expects before going all cute with names like ɹǝsn or εξέρχεται.
Make renaming easy, and do it often.
Perfection is achieved, not when there is nothing left to add but when there is nothing left to take away...
Implement the general case, with the policy information as an example of the type of thing the system needs to support.
This example also illustrates our belief that successful tools adapt to the hands that use them.
Create and maintain a project glossary—one place that defines all the specific terms and vocabulary used in a project. All participants in the project, from end users to support staff, should use the glossary to ensure consistency. This implies that the glossary needs to be widely accessible—a good argument for online documentation.
It’s hard to succeed on a project if users and developers call the same thing by different names or, even worse, refer to different things by the same name.
The popular buzz-phrase “thinking outside the box” encourages us to recognize constraints that might not be applicable and to ignore them. But this phrase isn’t entirely accurate. If the “box” is the boundary of constraints and conditions, then the trick is to find the box, which may be considerably larger than you think.
Andy wrote an entire book about this kind of thing: Pragmatic Thinking and Learning: Refactor Your Wetware [Hun08]
I’ve never met a human being who would want to read 17,000 pages of documentation, and if there was, I’d kill him to get him out of the gene pool. Joseph Costello, President of Cadence
How do Committees Invent? [Con68]
Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations.
But most importantly, development teams that include users will produce software that clearly reflects that involvement, and teams that don’t bother will reflect that, too.
In fact, whenever someone says “do this, and you’ll be agile,” they are wrong. By definition.
The manifesto suggests that you do this by gathering and acting on feedback. So here’s our recipe for working in an agile way: 1. Work out where you are. 2. Make the smallest meaningful step towards where you want to be. 3. Evaluate where you end up, and fix anything you broke.
Build teams so you can build code end-to-end, incrementally and iteratively.
A great way to ensure both consistency and accuracy is to automate everything the team does. Why struggle with code formatting standards when your editor or IDE can do it for you automatically? Why do manual testing when the continuous build can run tests automatically? Why deploy by hand when automation can do it the same way every time, repeatably and reliably?
Read The Mythical Man Month [Bro96] by Frederick Brooks. For extra credit, buy two copies so you can read it twice as fast.
If a bug slips through the net of existing tests, you need to add a new test to trap it next time.
How will you know that we’ve all been successful a month (or a year, or whatever) after this project is done?
In the second chunk of code, each step returns an object that implements the next function we call: the object returned by content_of must implement find_matching_lines, and so on. This means that the object returned by content_of is coupled to our code. Imagine the requirement changed, and we have to ignore lines starting with a # character. In the transformation style, that would be easy: