This is a popular book from 1999 y. which don’t lose its value even now was created by coders who did their applications in 80-90th. It looks like authors got their success as writers or speakers more than by actually coding something. As the title entails it is about how to become productive programmer, rules for it. Those vary from general (like fix a ‘broken window’) to specific ones (like use postconditions). Most of the time it is better to think out your examples to connect dots, to remember it but still examples from the book can be taken also. It has some enlightening thoughts about programming philosophy, e.g. it states that the main matter we mangle with is plain text thus we have to master right tools. I use rules from this book in my everyday work and strongly advice this to any programmer regardless of her programming language or platform. So below are my notes from this book.
- The cat ate my source code. Provide options, don’t make lame excuses. Take responsibility.
- Software Entropy. Don’t live with broken windows.
- Stoned soup and boiled frogs.
- Be catalyst for change. Permissions are not given then try it yourself. Excuses are cheaper then permissions.
- Keep tracking of the big picture. Remember the slowly boiled frog as it didn’t track temperature raise.
- Don’t Repeat Youself (DRY). It can be because it seems to be no other way (documentation and code) or it can be unseen (objects has the same field) or developers don’t want to not duplicate or there are many progers).
- Eliminate effect between unrelated parts (make that orthogonal, change in one doesn’t effect the other part) (that is similar to abstraction).
- (Examples, benefits, non-orthogonal systems minuses, better/worse technologies)
- While coding: avoid global data (and singletons), similar functions (check design) and coupled code.
- Assessment of orthogonality: unit tests don’t touch many objects, bug fixes don’t touch many files/objects. Make tags in GIT to assess how many files were touched on a bug fix (monthly assessments)
- Reversibility — the implementation way with possible future total change.
- Make reversible decisions: isolate database, client-server to standalone and back reversible application, isolate OS calls, etc. No decision is final.
- Tracer Bullets
- Use tracer bullets to get feedback (target) quickly
- Tracer bullet is the same production code but without all functionality there. All parts connected. It is skeleton on which further parts are put.
- (See pluses: quick demo, integration environment, etc.)
- Prototyping and Post-It notes
- Domain languages
- About importance of estimating
- Estimate to avoid surprises
- Choose worse accuracy for longer periods (e.g. 120 days -> “about 6 weeks”).
- Ask someone who already done this
- Build a model of the system. For project: what steps to do (prerequisites, coding, testing, etc.)
- Don’t dismiss strange estimation. Check understanding of model.
- Keep track of estimates and analyse them.
- Iterate schedule with code
- It is almost imposable to give correct estimation on start.
- Gain estimations as project goes. Run project in iterations. Estimate each iteration.
- Don’t give quick estimates
In short: It convinces for using plain text, shell, one power editor, version control system and other.
- Don’t use single IDE. Get know basic tool set.
- Keep knowledge in plain text. Plain text is a self-explanatory and understandable text. Could be structured (XML, html, etc.).
- Advantages: it doesn’t get obsolete, it can be taken by many tools. And it can drive tests (?).
- Shell games.
- Use shell to work with OS. Advantages: commands can be combined, and other. GUI is limited to what is shown.
- Power editting.
- Use one power editor…
- Source code control
- Fix the problem, don’t blame.
- Think about problem and don’t panic.
- Fix compiler warnings first.
- Text manipulation (scripts)
- Learn text manipulation language : awk, sed, Perl, TCL, Python, etc.
- Code generators (CG)
- Write code that writes code. That will reduce complexity and chance of mistake.
- Use passive CG to produce beginning of source code (e.g. Resharper) and for other
- Use active CG to synchronise things with each other. E.g. ORM or two different languages. And other. Active CG doesn’t break DRY principle.
Intro. You can’t write perfect code. Protect code from itself and from other code. As on road, none is sure above actions of others.
- Design by Contract (DBC) ( http://research.microsoft.com/en-us/projects/contracts/ and http://blogs.jetbrains.com/dotnet/2012/08/contract-annotations-in-resharper-7/)
- Preconditions — a state of “world” in order to run routine. It is responsibility of a caller to pass good data.
- Postconditions — a state of “world” after routine has done work.
- Class invariants — something that should not change before and after routine execution in class.
- Program should do something no more no less. Contracts are for this insurance.
- Design with Contracts to adhere Orthogonality principle (? p.111)
- Use contracts to support Liscov Substitution principle. That is that a sub class can really be used as base class. It is very good suited for inheritance and polymorphism.
- Use DBC not to get into programming by coincidence.
- Use assertions if DBC is not supported. Assertions can’t substitute DBC.
- DbC supports crashing early.
- Invariants can be used to check values in loops.
- Inviolate requirements (laws) are thought as semantic contracts, project invariants.
- Dead Program Tells No Lies.
- Crash early so that no damage done.
- Assertive Programming
- Use assertions for situations that could never happen.
- Use assertions for prod also. Remove only critical for performance.
- When To Use Exceptions
- Use them only for exceptional situations, unexpected.
- Use error handlers (routine called on exception) instead of exceptions if they can’t be used (in C, in remote calls when it is a lot of duplicates.
- How to Balance Resources
- Deallocate resources in object or routine that allocated them. See example about file opening and closing.
- (See rules for nest allocation, also about deallocation in C++ and Java.)
- (About inability to balance resources…)
- Decoupling and the Law of Demeter
- Design code such that modules (cells) don’t interact with others much. Or decrease coupling.
- Use Law of Demeter: any function should only call methods of 1) self object, 2) passed params, 3) directly held objects (static in C#); 4) created in the object objects.
- Balance usage of the Law of Demeter. It leads to many wrapper methods and hence higher resource usage; on the other side, not using it leads to unflexible but performance design.
- (Also physical coupling should be reduced in large scale projects?)
- Use metadata, don’t integrate. Use it everywhere. Get rid of details: kind of UI, algorithm choice and other.
- Metadata is data about data (e.g. database scheme), it is used and accessed at run time. It can be key/value pairs or some language (domain languages).
- Put abstractions into code, details in metadata. Think declaratively (?).
- Use metadata to configure even business logic (see Domain languages).
- If flexibility required consider rereading metadata in runtime.
- Temporal coupling — couplings things or model in time.
- (About designing for concurrency.)
- It’s Just a View
- Solving the problem of decoupled objects communication with publish/subscribe protocol (observer pattern).
- Event —
- Model — abstract data itself.
- View — representation of data, subscribes to changes in model and logical events in controller.
- Controller — to control a view and provide a new data to model. Highly coupled with view.
- Separate views from models.
- Use MVC not only for GUI but it can be use in more general way.
- Blackboard — a space with consistent access where any object can access it to put/read/take or do smth else. Blackboard triggers event like smth put. Blackboard can be partitioned to ease data access.
- Use blackboard to coordinate workflow. (Helmes? Unit Of Work?)
- NExt: exerciess on 170
- Programming by coincidence
- It is programming on luck, not taking responsibility for all possible ways the code can run. Not thinking about what you write. Not knowing problem or requirements to the end before starting, not knowing context. Also it is not documenting assumptions. It is relying on unreliable things.
- Don’t program by coincidence
- Program deliberately: be aware of the code, and other.
- Algorithm speed
Use O() notation — order of an algorithm (See examples of O(1), O(n), O(n^2) etc))
Know about common algorithms (common sense):
- Get item from array — O(1)
- Loops — O(n)
- Loop in loop (bubble sort) — O(n*m)
- Quicksort (divine and conquer) — O(n*lg(n))
Estimate the Order, O() of an algorithm
Check you estimates
Don’t choose inappropriate algorithm
Don’t waste time on unneeded optimization for now.
- Best metaphor for software development is gardening, not building construction. I can get out bad plants, move some plants to other places, get split some big plants, etc.
- Say to customers: refactoring is like managing “growth” (cancer form). If it is managed now it will grow and kill organism or it will be harder to remove it later.
- Refactor often, refactor early.
- Don’t refactor and add new functionality at once.
- Test often, test what is being refactored.
- Refactor in small steps
- Code That’s Easy To Test
Like circuit a code can be tested.
Do it from isolated unit tests to tests as a whole.
Test against code contracts (border cases, non-supported values, smth other)
Design system to test: modularity.
- for small systems: in a class itself
- bigger: in a subdirectory
Use Test Harness for large projects (nUnit, jUnit etc).
Create test cases from debugging data.
Build a Test Window:
- log files,
- pop-up windows on hot-keys for help desk.
- web server built into application to get full info.
Test Your Software or users will.
- Evil wizards
- Don’t use wizards (XSD) you don’t understand. That is because this code becomes your own. The third libraries are not.
- The Requirements Pit
- Strive for perfection by removing.
- Dig for requirements
- Requirement — abstract statement of a need; it is a need.
- Policy — vague statement with details, an example of a need.
- Document requirements and policies separately. Add hyper-links.
- Dig for underlying problem, need, reason, why they want it. Become an user.
- Document requirements using use cases. Use case — use of a system in abstract fashion, with describing an aim.
- (see Use case example and template)
- Nest use cases from general to specific ones.
- Use UML use case diagrams.
- Stay abstract. Beware of over-specifying.
- Use project glossary.
- Track requirements number and their impact on the system (LOC, bugs, etc.)
- Publish requirements for all members.
- (see challenges and exercise — UNDONE)
- Solving impossible puzzles
- Find a box. Define real constraints. Check that they are for sure real.
- Not until you’re ready
- Listen to nagging doubts, heed it. Prototype to find you whether it was procrastination or a real hidden problem.
Circles and Arrows
- (SKIPPED) (About how important design tools are. They are not important.)
- Pragmatic Teams
- (The same as in previous paragraphs but for teams)
- Ubiquitous Automation
- Automate everything
- Don’t use manual procedures (people get it wrong)
- Use schedulers (cron at Linux, at in Windows)
- Things to automate:
- regression tests
- night builds
- final builds
- administration tasks (emails, other?)
- Common web site (like pandoc for view on all docs and comments; and for build logs).
- … and other
- Ruthless Testing
Be eager to find bugs.
Test often. Test early. Test automatically.
Test code can be bigger than work code.
Coding ain’t done, till all tests run.
What to test:
- Units. Unit tests. Units meet their contracts. Must
- Subsystems, system. Integration tests. Subsystems meet their contracts. Must
- Meeting of users’ needs. Validation and verification.
- Cases of the exhaustion of resources: errors, recovery.
- Performance. In real-world. In a need of more resources, is it scalable?
- Usability. By real users. As early as possible.
How to test
- Regression tests — tests run after functionality implemented. All automated tests.
- Test data. Can be real-world and synthetic.
- For GUI — automating tools. Decouple system so that model errors are found before view errors. Then it is easier to test GUI.
- Test the tests. Ensure that tests break in case of error. Then the tests work.
- Test state coverage, not code coverage. Number of states is huge. It is big problem.
When to test
- Test as soon as production code appears.
- Test before checking in.
- Make stress tests on regular basis.
Add test if a bug found. Find bugs once. Then it is the last time the bug found.
- It’s All Writing
Do not duplicate effort while writing documentation. Use pragmatic advices from here. Treat English as Just another Programming language.
Keep it close at hand.
Two kinds of documentation. Internal documentation — source code, comments, design and other. External doc — user manuals, shipped to outside.
Build Documentation in, Don’t bolt it on. (?)
- Comment why it is done so, not about how it is done (it is already in source code). In module level (assembly), data, type declarations, per-class, per-method.
- Use meaningful variable names. Don’t mislead by var. names.
- Leave in comments: author’s name, copyright notices. Can leave in comments: data of the last change.
Source code, documents, database tables are all views of a model (see It’s just a view). Choose one main source and generate other views from it.
Technical writers should also use basic principles of Pragmatic Programmers.
Produce documentation that can be kept up to date. Produce it automatically. Don’t violate DRY by copy-pasting. Paper is hard for it.
Use some markup language for doc. DocBook. (Markdown?)
- Great expectations
- Gently Exceed Your Users’ Expectations.
- Communicate users.
- Don’t manage expectations.
- Everyone should understand what is expected and how it will be built.
- Give a little bit more.
- Pride and Prejudice.
- Sign your work.
- Прагматизм (от др.-греч. πράγμα, родительный падеж πράγματος — «дело, действие») — философское течение, базирующееся на практике как критерии истины и смысловой значимости.
Last updated at 2017-07-14