The Pragmatic Programmer

Table of Contents

From Journeyman to Master

Preface

  • core process: taking a requirement and producing working, maintainable code that delights the user.
  • QWAN - quality without a name. Don’t preach, just tell/do what works.
  • pragmatic -> lat. (pragmaticus) -> greek “to do”.
  • There are no easy answers There is no such thing as best solution, tool language or OS.
  • You shouldn’t be weeded to any particular technology but have broad enough background and experience base to allow to choose good solution in any particular situation.

What Makes a Pragmatic Programmer?

  • Early Adopter & Fast Adapter - instinct for technologies and techniques, love to try things out, grasp it quickly and integrate with the rest of the knowledge. Your confidence is born out of experience.
  • Inquisitive - you tend to ask questions (TODO). You are a pack rat for little facts.
  • Realistic - try to understand the underlying nature of each problem you face.
  • Jack of all trades - always able to move on to new areas and new challenges.

TIP 1: care about your craft
TIP 2: Think! About your work. Think what you are doing while you’re doing it. Never run on autopilot. Constantly be thinking, critiquing your work in real time.

  • Quarry workers’ creed: We who cut mere stones must always be envisioning cathedrals
  • It’s a continuous process. Kaizen: continuously making many small improvements. Every day work to refine the skills you have and add new tools to your repertoire.

Chapter 1. A Pragmatic Philosophy

The Cat Ate My Code

  • The greatest of all weaknesses is the fear of appearing weak
  • Take responsibility. In addition to your personal best you must analyze the situation for risks that are beyond your control

TIP 3: Provide options, don’t make lame excuses

Software Entropy

  • One broken window, left unrepaired for any substantial length of time, instills in the inhabitants of the building a sense of abandonment. A sense that the powers that be don’t care about the building -> littering, graffiti -> structural damages -> a sense of abandonment becomes reality.
  • Don’t leave “broken windows”: bad designs, wrong decisions, poor code. Fix as soon as you discover. If you don’t have the time to fix properly board it up (TODO, XXX …).

Putting out fires

  • If you find yourself working on a project with a few broken windows it’s all to easy to just follow suit. By the same token, if you find yourself in a project where the code is beautiful, you’ll likely take extra care not to mess it up.
  • TODO: Help strengten your team by surveying your company “neighborhood”. Choose two or three broken windows and discuss with colleagues what the problems are and what can be done to fix them.

Stone Soup and Boiled Frogs

  • It’s easier to ask for forgiveness than to get permission
  • Stone Soup parable: People find it easier to join an ongoing success. Show them a glimpse of the future and you’ll get them to rally around. Let them dream. “This can be added later” - in case they lack something.

TIP 5: Be a catalyst for change.
TIP 6: Remember the big picture. Don’t be like the frog (Boiled Frog that doesn’t notice the gradual change in it’s environment). Constantly review what’s happening around you and not only what you are personally doing.

Good Enough Software

  • Doesn’t mean sloppy or poorly produced code. All systems must beet their users’ requirements to be successful.
  • Involve your users in the trade-off
  • Remember it’s equally unprofessional to promise an improbable time scale and to cut base engineering corners to meet a deadline.

TIP 7: Make Quality a Requirements Issue

  • Know when to stop: don’t spoil a perfectly good program by overembellishment or over-refinement. Example: OS producers ship even though they know their SW is not perfect and will have patches in future.

Your Knowledge Portfolio

  • Your knowledge and experience are your most important - and expiring - assets.
  1. Invest regularly, as a habit.
  2. Diversify. Know the ins and outs of the particular technology you are working with ATM. Don’t stop there
  3. Manage risks. Don’t put all your technical eggs in one basket.
  4. Buy low, sell high. Learn emerging technologies before they become popular.
  5. Review and balance.

TIP 8: Invest Regularly in Your Knowledge Portfolio

Goals:

  • TODO: Learn at least one new language each year
  • TODO: Read a technical book each quarter. Then each month.
  • Read non-technical books too.
  • TODO: Take classes.
  • Participate in local user groups.
  • TODO: Experiment with different environments, tools, technologies.
  • TODO: Stay current - subscribe to trade magazines and other journals
  • Get wired

Critical Thinking: never underestimate the power of commercialism. Just because a search engine lists a hit first it doesn’t mean it’s the best match.

TIP 9: Critically Analyze What You Read and Hear.

  • TODO: Challenge: Get out and talk technology with people who aren’t involved in your current project or don’t work in your company.

Communicate!

  • Having the best ideas, the finest code, or the best pragmatic thinking is ultimately sterile unless you can communicate w. other people. A good idea is an orphan without effective communication.

Know What You Want To Say

  • probably the most difficult part of the more formal styles of communication in business is working out exactly what is it you want to say.
  • Plan what you want to say. Write an outline. The ask yourself: “Does this get around whatever I’m trying to say?” Refine until it does

Know Your Audience

  • You need to understand the needs, interests and capabilities of your audience.

  • Communicating vs. talking, which is annoying. Annoy - from old french “enui” (means also “to bore”).

  • Wisdom acrostic:

                      |W|hat do you want them to learn?
        What is their |i|nterest in what you've got to say?
                  How |s|ophisticated are they?
             How much |d|etail do they want?
    

    Whom do you want to |o|wn the information? How can you |m|otivate them go listen to you?

  • By making the appropriate pitch to each group, you’ll get them all excited about your project.

Choose Your Moment: Sometimes all it takes is the simple question: “Is this a good time to talk about …?”

Choose Your Style - format / just the facts / … suit it your audience.

Make It Look Good: Any chef will tell you that you can slave in the kitchen for hours only to ruin your efforts with poor presentation.

Involve Your Audience: If possible, involve your readers with early drafts of the document. Get their feedback and pick their brains.

Be a Listener

  • If you don’t listen to them, they won’t listen to you.
  • Encourage people to talk by asking questions or have them summarize what you tell them.
  • Turn the meeting into a dialogue and you’ll make your point more efficiently.

Go Back to People: Always respond to e-mails and voice mails, even if your response only says “I’ll get back to you later”.

TIP 10: It’s both, what you say and the way you say it.

  • The more effective the communication, the more influential you become.
  • Mails:
    • Proofread before you send.
    • Check the spelling.
    • Keep the format simple.
    • Minimize quoting.
    • Organize and archive them.

Chapter 2. Pragmatic Approach

The Evils of Duplication

  • Knowledge isn’t stable. It changes rapidly -> we spend large part of our times in maintenance mode. Programmers are constantly in maintenance mode.
  • It’s easy to duplicate knowledge in specifications, processes and programs -> maintenance nightmare.
  • DRY principle: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

TIP 11: DRY

  • Mitigations

    • code generators. The trick is to make the process active, not a one time conversion which would be duplicating data again.
    • Documentation in code: untrustworthy comments are worse than no comments at all.
    • Whatever the situation, avoid unnormalized data.
  • Shortcuts make for longer delays

  • Hardest type of duplication occurs between different developers on a project

    • frequent communication
    • appoint a librarian

TIP 12: Make it Easy to Reuse

Orthogonality

  • Two or more thing are orthogonal if changes in one do not affect any of the others.
  • Nonorthogonal systems are more complex to change and control. In such a system there is no thing as a local fix.

TIP 13: Eliminate Effects Between Unrelated Things

  • write small, self-contained components
  • promote reuse. Components have specific, well-defined responsibilities.
  • If two components are not orthogonal, there will be overlap and the result will be less. You get more functionality pre unit effort by combining orthogonal components.
  • Orthogonal approach reduces risk - diseased sections of code are isolated and it’s easier to slice something out and transplant something new and healthy -> less fragile system, easier testing, less tightly coupling.
  • Same applies for Project Teams. If there is a lot of overlap, there is confusion about responsibilities. Every change needs a meeting of the entire team, because any one of them might be affected.
  • If I dramatically change the requirements behind a particular function, how many modules are affected? In an orthogonal system, the answer should be “one”.
  • Don’t rely on properties of things you can’t control.

Coding

  • Write shy code. Modules that don’t reveal anything unnecessary to other modules. If you need to change an object’s state, get the object to do it for you.
  • Law of Demeter
  • Avoid global data
  • Be careful with singletons - they can also lead to unnecessary linkage.
  • Avoid similar functions.
  • Refactor - look for any opportunities to reorganize your code to improve it’s structure and orthogonality.
  • every module should have unit tests and that tests should be performed automatically as part of the build process

Reversibility

  • Critical decision aren’t easily reversible.
  • If you really abstracted the idea of a [database] out - to the point where it simply provides [persistence] as a service - then you have the flexibility to change horses in midstream.
  • Instead of caring decisions in stone, think of them as being written in the sand at the beach. A big wave can come along and wipe them out at any time. (Flood, Sand Mandala)

TIP 14: There Are No Final Decisions

  • Architecture: Whatever mechanism you use, make it reversible.

Tracker Bullets (Agility)

  • Like gunners, you’re trying to hit a target in the dark. Dealing with lack of knowledge, experience … The classic response is to specify the system to death. One big calculation, then shoot and hope.
  • Code That Glows in the Dark: we’re looking for something that gets us from requirement to some aspect of the final system quickly, visibly, and repeatably.

TIP 15: Use Tracer Bullets to find the Target

  • Tracer code is not disposable. It contains all the error checking, structure, documentation … that any piece of production code has. It is simply not fully functional.
  • Tracer development is consistent wit the idea that a project is never finished. There will be always changes required and functions to add. It is an incremental approach.
  • Advantages
    • Users get to see something working early. You have something to demonstrate.
    • Developers build a structure to work in.
    • You have an integration platform.

Tracer Code vs. Prototyping

  • Prototype = reconnaissance and intelligence. With a prototype you are aiming to explore specific aspects of the final system. A true prototype will be always thrown out and you recode the system properly applying the lessons you’ve learned.
  • Tracer code is lean but complete. Forms the skeleton of the final system. Helps you to know how the application as a whole hangs together.

Prototypes and Post-it Notes

  • What to investigate with a prototype? Anything that carries risk. Anything unproven, experimental, doubtful.
  • Prototyping is a learning experience. Its value is in the lessons learned, not in the code produced.
  • Make absolutely clear, that the code is disposable, incomplete and unable to be completed. If not possible to convey, use tracer coding instead.

TIP 16: Prototype to learn

  • When prototyping, you can ignore correctness, completeness, robustness, style.
  • Prototyping architecture: what you’re looking for is how the system as a whole hangs together, again deferring details.

Domain Languages

The limits of language are the limits of one’s world. – Ludwig von Wittgenstein

  • The language of the problem domain might also suggest a programming solution.
  • DSL - you’re giving yourself a tool that let’s you work close to the problem domain (tetX, ply, TTTech’s code generator). By coding at a higher level of abstraction, you’re free to concentrate on solving domain problems and you can ignore implementation details.

TIP 17: Program Close to the Problem Domain

  • If you are writing in the problem domain, report problems in terms the user can understand (perform domain specific validation).
  • Most applications exceed their expected lifetimes. In most cases you just might be better of adopting a more complex and readable language up front. The initial effort will be repaid many times in reducing support and maintenance costs.

Estimating

TIP 18: Estimate to Avoid Suprises

  • The units you use a make a difference in the interpretation of the result (130 days vs about 6 months).
  • Basic estimating trick: always ask somebody who’s already done it first.
  • Understand What’s Being Asked: make it a habit to think about the scope before starting to guess.
  • You’ll often find yourself basing an estimate on other subestimates. That’s where the largest errors come from.
  • The only way to determine the timetable for a project is gaining experience on the same project.

TIP 19: Iterate the Schedule with the Code

  • What to Say when Asked for an Estimate: I’ll get back to you

Chapter 3: The Basic Tools

  • Tools amplify your talent
  • Keep the basic tool set sharp and ready to use.

The Power of Plain Text

  • Your base material isn’t wood or iron, it’s knowledge. Best format format for storing knowledge persistently is plain text. It gives the ability to manipulate knowledge, both manually and programmatically, using any tool at our disposal.
  • The problem with most binary formats is that they divorce data from its meaning.

TIP 20: Keep Knowledge in Plain Text

  • Larger and slower vs. Insurance against obsolescence, VCS + diff, easier testing.
  • Human readable forms of data, and self-describing data, will outlive all other forms of data and applications that created them. Period
  • Difference between human readable and human understandable.

Shell Games

  • For a programmer manipulating files of text, the workbench is the command shell.
  • Doing all the work using GUIs, you’ll miss the full capabilities of your environment. WYSIWYG - what you see is what you get. What you see is all you get.

TIP 21: Use the Power of Command Shells

Power Editing

  • One Editor - better know one editor well and use it for all editing tasks.
  • Editor Requirements: configurable, extensible, programmable.
  • Automation: For anything you find yourself doing repeatedly, develop a set of macros (or equivalent) to handle it.
  • Try to accomplish any given editing task in as few keystrokes as possible.

Source Code Control

  • Those who cannot remember the past are condemned to repeat it.

TIP 23: Always Use Source Code Control

Debugging

  • modern computers are still limited to doing what you tell them to do, not necessarily what you want them to do.
  • In the technical area, you want to concentrate on fixing the problem, not the blame. It doesn’t really matter whether the bug is your fault or someone’s else. It is still your problem.

TIP 24: Fix the Problem, Not the Blame

A Debugging Mindset

  • The easiest person to deceive is one’s self

TIP 25: Don’t Panic

  • Before you start looking for the bug, make sure to turn on the highest level of warnings and that the code compiles cleanly without any warnings.
  • You must brutally test both the boundary conditions and realistic end-user usage patterns.
  • Tracing: debuggers fous on the state of the program now. Tracing helps you see what happened in the past.
  • Rubber ducking - explaining the cause of a problem to someone else - helps (you to see, what you’ve taken for granted).

TIP 26: select() Isn’t Broken

  • Binary search: if you have no obvious place where to start looking from.

TIP 27: Don’t Assume It - Prove it

  • If the bug is the result of bad data propagated through a couple of levels before causing the explosion, see if better parameter checking in those routines would have isolated it earlier.
  • While you’re at it, see if there are ny other places susceptible to this same bug. If so, fix them now.
  • Debugging Checklist
    • bug or merely a symptom
    • Is the bug really in the compiler? In the OS? Or is it in your code?
    • If you explained this problem in detail to a coworker, what would you say?
    • If the suspected code passes its unit tests, are the tests complete enough? What happens if you run them with this data?
    • Do the conditions that caused this bug exist anywhere else in the system?

Text Manipulation

TIP 28: Learn a Text Manipulation Language

  • perl, ruby, awk, sed, python, tcl…

Code Generators

  • Take away complexity and reduce the chances of making mistakes, leaving the craftsman free to concentrate on quality. Just like woodworkers using a template when producing the same thing over and over.

TIP 29: Write Code That Writes Code

  • Passive code generators are run once to produce a result. From that point forward, the result becomes freestanding. They save typing, they are basically parametrized templates.
  • Active code generators run each time their result is required. While passive generators are a simple convenience, active ones are necessity if you want to follow DRY principle.
    • Whenever you need to get two disparate environments working together, consider using active code generators.

Chapter 4. Pragmatic Paranoia

TIP 30: You Can’t Write Perfect Software

  • Pragmatic Programmers don’t trust themselves either.

Design by Contract

  • A contract defines your rights and responsibilities, as well as those of the other party. In addition, there is an agreement concerning repercussions if either party fails to abide the contract.
  • DBC: if all the routine’s preconditions are met by the caller, the routine shall guarantee that all postconditions and invariants will be true when it completes.

TIP 31: Design with Contracts

  • Remember, if your contract indicates that you’ll accept anything and promise the world in return, then you’ve got a lot of code to write!
  • Liskov Substitution Principle: Subclasses must be usable through the base class interface without the need for the user to know the difference.
  • Even w/o automatic checking (Eifell), you can put contract in the code as comments. If nothing else, the commented contracts give you a place to start looking when trouble strikes.
  • It’s much easier to find and diagnose the problem by Crashing Early, at the site of the problem.
  • If DBC is so powerful, why isn’t it used more widely? Is it hard to come up with the contract? Does it make you think about issues you’d rather ignore for now? Does it force you to THINK? Clearly, this is a dangerous tool!

Dead Programs Tell No Lies

  • Sometimes other people can detect things aren’t well with you before you yourself are aware of the problem. It’s the same with other people’s code.
  • It’s easy to fall into “it can’t happen” mentality.

TIP 32: Crash Early

  • When your code iscovers that something that was supposed to be impossible just happened, your program is no longer viable. Anything it does from this point forward becomes suspect, so terminate it as soon as possible. A dead program does a lot less damage than a crippled one.

Assertive Programming

There is a luxury in self-reproach. When we blame ourselves we feel no one else has a right to blame us. – Oscar Wilde, The Picture of Dorian Gray

TIP 33: If it Can’t Happen, Use Assertions to Ensure That it Won’t

  • Assertions can be turned off -> never put code that must be executed into an assert.
  • Assertions don’t replace real error handling. They check for things that should never happen.
  • Your first line of defense is checking for any possible error, and your second is using assertions to try to detect those you’ve missed.
  • Even if you do have performance issues, turn off only those assertions that really hit you.
  • “Heisenbug” - debugging that changes the behaviour of the system being debugged.

When to Use Exceptions

  • “Will this code still run if I remove all the exception handlers?” If the answer is “no”, then maybe exceptions are bing used in nonexceptional circumstances.

TIP 34: Use Exceptions for Exceptional Problems

  • exceptions represent a kind of cascading goto -> spaghetti code -> maintainability problems.

How to Balance Resources

TIP 35: Finish What you Start

  • the routine or object that allocates a resource should be responsible for deallocating it.
  • Nest Allocations
    • Deallocate resources in the opposite order to that in which you allocate them. (stack)
    • When allocating the same set of resources in different places in your code, always allocate them i the same order. This will reduce the possibility of deadlock.
  • Balancing and Exceptions: finally.
  • Checking the Balance: it is a good idea to build code that actually checks that resources are indeed freed appropriately. For most applications, this normally means producing wrappers for each type of resource, and using these wrappers to keep track of all allocations and deallocations.

Chapter 5. Bend or Break

  • we need to make every effort to write code that’s as loose - as flexible - as possible.

Law of Demeter

Good fences make good neighbours – Robert Frost, “Mending Wall”

  • Write “shy” code: a) don’t reveal yourself to others b) don’t interact with too many people.
  • Spies, dissidents, revolutionaries are organized into small groups of cells. Organize your code into cells and limit the interaction between them. If one module get’s compromised and has to be replaces, the other modules should be able to carry on.
  • When we ask an object for a particular service, we’d like the service to be performed on our behalf. We do not want the object to give as a third-party object that we have to deal with to get the required service (general contractor - house construction).
  • Rather than digging through a hierarchy yourself, just ask what you need directly.
  • Law of Demeter: any method of an object should call only methods belonging to a) itself b) any parameters that were passed in to the method c) any objects it created d) any directly held component objects

TIP 36: Minimize Coupling Between Modules

Metaprogramming

No amount of genius can overcome a preoccupation with detail – Levy’s Eight Law

  • Make our code highly configurable and soft - i.e. easily adaptable to changes.
  • Items (middleware, DB, algorithms …) should be implemented as configuration options, not through integration or engineering.

TIP 37: Configure, Don’t Integrate

  • Use metadata that describes the application. Our goal is to think declaratively (and specifying what is to be done, not how - e.g. DI). Program for the general case, and put specifics somewhere else - outside the compiled code base.

TIP 38: Put Abstractions in Code Details in Metadata

  • We want to defer definition of most details until the last moment
  • Configure business logic by writing rules, not cutting code. Less complex logic can be expressed using a mini-language.
  • Software that adapts itself to its environment
  • Without medatadata, your code is not as adaptable or flexible as it could be.

Temporal Coupling

  • Workflow: find out what can happen at the same time, and what must happen in strict order. Think line in workflow diagrams is called synchronization bar.

TIP 39: Analyze Workflow to Improve Concurrency TIP 40: Design Using Services

  • Instead of components, we have really created services - independent, concurrent objects behind well-defined, consistent interfaces.
  • Design for Concurrency: with linear code, it’s easy to make assumptions that lead to sloppy programming (programming by coincidence). But concurrency forces you to think through things a bit more carefully - you’re not alone at the party anymore. To begin with, any global or static variables must be protected from concurrent access.

TIP 41: Always Design for Concurrency

  • By architecting your system as independent services, you can make the configuration dynamic as well.
  • Going the other way (try to add concurrency to a non-concurrent app) is much harder.

It’s Just a View

  • A good definition of a module (or class) is that it has a single, well-defined responsibility.
  • Using events to minimize coupling between objects - so they don’t need to know much about each other. The sender doesn’t even have to have explicit knowledge of the receiver. There can be even multiple receivers.
  • Publish/Subscribe. Use this mechanism to separate the views from the model. This is one of the most important ways of maintaining reversibility.

TIP 42: Separate Views from Models

Blackboards

  • Consider how detectives might use a blackboard to coordinate and solve a case.
  • A blackboard system let’s us decouple our objects from each other completely, providing a forum where knowledge consumers and producers can exchange data anonymously and asynchronously.
  • methods:
    • read (retrieve data from space)
    • write (put item into the space)
    • take (retrieve and remove an item from the space)
    • notify (set up a notification to occur whenever an object is written that matches the template)
  • On large cases, the blackboard may become cluttered -> partition.

TIP 43: Use Blackboard to Coordinate Workflow

  • We can use the blackboard to coordinate disparate facts and agents, while still maintaining independence and even isolation among participants.

Chapter 6. While You Are Coding

  • Developers who don’t actively think about their code are programming by coincidence - the code might work, but there’s no particular reason why. Avoid programming by coincidence - relying on luck an accidental success - in favour of programming deliberately.
  • For code you write that others will call, the basic principles of good modularization and of hiding implementation behind small, well-documented interfaces can al help. For routines you call, rely only on documented behaviour.
  • Assumptions that aren’t based on well-established facts are the bane of all projects.

TIP 44: Don’t Program by Coincidence

How to Program Deliberately

  • Always be aware of what you are doing.
  • Don’t code blindfolded. Attempting to build an application you don’t fully understand, or to use a technology you aren’t familiar with.
  • Proceed from a plan.
  • Rely only on reliable things. Don’t depend on accidents or assumptions. If you can’t tell the difference in particular circumstances, assume the worst.
  • Document your assumptions.
  • Write an assertion to test your assumptions.
  • Prioritize your effort. Spend time on important aspects.
  • Don’t let existing code dictate future code. All code can be replaces if no longer appropriate. Refactoring assumption: the impact will be less than the cost of not making the change.

Algorithm Speed

  • Normally, the size of the input will affect the algorithm, the larger the input, the longer the running time or the more memory used. Most significant algorithms are not linear.
  • If you write Loops or recursive calls, check subconsciously for runtime and memory requirements.
  • O() Notation. Worst-case time. Upper bound. It is convention to remove from it all low-order terms and do not bother showing any constant multiplying factors. O(n2/2+3n) -> O(n2/2) -> O(n**2)
  • Things start getting out of hand quickly once we get over O(n**2):
    • O(1) constant
    • O(lg(n)) Logarithmic (binary) search (Binary chop).
    • O(n) linear
    • O(n lg(n)) not too much worse than linear (quicksort, heapsort). Divide and Conquer.
    • O(n**2) square law
    • O(n**3) cubic
    • O(C**n) exponential (Traveling salesman problem). Combinatoric( e.g. 5!) -> use heuristics.

TIP 45 Estimate the Order of Your Algorithms

  • After all this estimating, the only timing that counts is the speed of your code, running in production environment, with real data.

TIP 46: Test Your Estimates

  • Danger of premature optimization: make sure an algorithm is really a bottleneck before you invest previous time improving it.

Refactoring

  • Rather than construction, software is more like gardening - more organic than concrete. You plant things in your garden according to an initial plan, then you constantly monitor the health of the garden, making adjustments as needed. Things that don’t work out as planned need to be weeded / pruned.
  • When refactor? Don’t hesitate - there is no time like the present. Common reasons: Duplication, non-orthogonal design, outdated knowledge, performance.
  • Refactoring - like surgery, take it out now or let it grow and deal with major trouble.

TIP 47: Refactor Early, Refactor Often

  • Don’t try to refactor and add functionality at the same time.
  • Make sure you have good tests before you begin refactoring.
  • Take short, deliberate steps so you avoid prolonged debugging.

Code That’s Easy to Test

  • Chips are designed to be tested, not just at the factory, not just when they are installed, but also in the field when they are deployed. Like our HW colleagues we need to build testability into SW from the very beginning, test each piece thoroughly before trying to wire them together.
  • Think of unit testing as testing against contract. We want to write test cases that ensure that a given unit honors its contract.
  • Pass in value that is to be rejected, pass in boundary values that are still to be accepted. Pass in correct values.
  • Testing requires you to test subcomponents of a module first. Once the subcomponents have been verified, then the module itself can be tested.

TIP 48: Design To Test

  • When you design a module, or even a single routine, you should design both its contract and the code to test that contract. By designing code to pass a test and fulfill its contract, you may well consider boundary condition and other issues that wouldn’t occur to your otherwise.

Writing Unit Tests

  • Make the test code readily accessible.
  • Providing unit tests isn’t enough. You must run them and run them often.
  • At the end of the debugging session you need to formalize the adhoc test. If the code broke once, it is likely to break again. Don’t throw away the test you created; add it to the existing uni tests.
  • Testing is more cultural than technical.

TIP 49: Test Your Software, or Your Users Will

Evil Wizards

TIP 50: Don’t Use Wizard Code You Don’t Understands

  • Have tools do only those things for you which you could do yourself.

Chapter 7. Before the Project

  • no method can replace thinking

The Requirements Pit

  • Requirements rarely lie on the surface. Normally, they’re buried deep beneath layers of assumptions, misconceptions, and politics.

TIP 51: Don’t Gather Requirements - Dig for Them

Digging for Requirements

  • gathering requirements leads to a system that is well factored to support metadata.
  • It’s important to discover why the users do a particular thing, rather than just how they do it. Documenting this reasons will give your team invaluable information when making daily implementation decisions.
  • Become a user. Request: “May I sit i for a week while you do your job?”
  • Management will give you one view of how things operate, but when you get down on the floor, you’ll find a very different reality - one that will take time to assimilate.

TIP 52: Work with a User to Think Like a User

Documenting Requirements.

  • Sometimes, the Interface Is the System. While slavishly duplicating what already exists doesn’t allow for progress, we must be able to provide a transition for future.
  • Using a formal template as an aide-memoire.
  • Use Case Diagrams: Don’t be a slave to any notation, use whatever method best communicates the requireements with your audience.

Overspecifying

  • good requirements document remain abstract. Where requirements are concerned, the simplest statement that accurately reflects the business need is the best.
  • Requirements are not architecture. They are not design, nor are they not user interface. Requirements are need.
  • See further

TIP 53 Abstractions Live Longer than Details

Just One More Wafer-Thin Mint

  • The key to managing growth of requirements is to point out each new feature’s impact on the schedule to the project sponsors.

Maintain a Glossary

  • Create and maintain a project glossary. All participants in the project, from end user to support staff, should use the glossary to ensure consistency.

TIP 54 Use a Project Glossary

  • It’s very hard to succeed on a project where users and developers refer to the same thing by different names, or even worse, refer to different things by the same name.
  • Get the Word Out - have your requirements in the WWW and make sure they are accessible all the project folks.

Solving Impossible Puzzles

  • The secret to solving the puzzle is to identify the real (not imagined) constraints and find a solution therein. Think outside of the box encourages us to recognize constraints that might not be applicable and to ignore them.

TIP 55: Don’t think Outside of the Box - Find the box

  • Categorize and prioritize your constraints. When woodworkers begin a project, they cut the longest pieces first, then cut the smaller pieces out of the remaining wood. In the same manner, we want to identify the most restrictive constraints first, and fir the remaining constraints within them.

There Must Be an Easier Way!

  • Is there an easier way?
  • Are you trying to solve the right problem, or have you been distracted by a peripheral technicality?
  • Why is this thing a problem?
  • What is it that’s making it so hard to solve?
  • Does it have to be done this way?
  • Does it have to be done at all?

All you need are the real constraints, the misleading constraints, and the wisdom to know the difference.

Not Until You are Ready

He who hesitates is sometimes saved. – James Thurber, The Glass in the Field

  • Great performers share a trait: they know when to start and when to wait

TIP 56: Listen to Nagging Doubts - Start When You’re Ready

  • Software development is still not a science. Let your instincts contribute to your performance.
  • Choose an area that you feel will be difficult and begin producing some kind of proof of a concept.

The Specification Trap

TIP 57: Some Things Are Better Done than Described

  • specification and implementation are simply different aspects of the same process - an attempt to capture and codify requirement. Let there be no artificial boundaries. A healthy development process encourages feedback from implementation and testing into the specification process.
  • The longer you allow specifications to be security blankets, protecting developers from the scary world of writing code, the harder it will be to move on to hacking your code out. Look at prototyping, consider a tracer bullet development (agile).
  • Sometimes a picture is worth more than any number of words. Sometimes it is worthless.

Circles and Arrows

  • Blindly adopting any technique without putting it into the context of your development practices and capabilities is a recipe for disappointment.

    TIP 58: Don’t Be a Slave to Formal Methods

  • Shortcomings:

    • We prefer to show the user a prototype and let him play with it instead of basing everything on designers’ explanations.
    • Formal methods encourage specialization. We prefer to understand the whole system we’re working on.
    • We like to write adaptable, dynamic systems using metadata to allow us to change the character of application at runtime. Most current formal methods combine a static object model or data model with some kind of event- or activity-charting mechanism.
  • Never underestimate the cost of adopting new tools and methods. Be prepared to treat the first projects as a learning experience.

  • Never become a slave to a methodology: circles and arrows make poor masters.

TIP 59: Expensive Too Do Not Produce Better Designs

  • If you come across a project where the philosophy is “the class diagram is the application, the rest is mechanical coding,” you know you’re looking at a waterlogged project team and long paddle home.

Chapter 8. Pragmatic Projects

Pragmatic Teams

  • No Broken Windows: Quality is a team issue. Teams as a whole should not tolerate broken windows - those small imperfections no one fixes. Some teams have a quality officer - ridiculous.
  • Boiled Frogs: Maybe appoint a chief water tester. Have this person check constantly for increased scope, decreased time scales, additional features, new environments - anything that wasn’t in the original agreement. Keep the metrics on new requirements. The team need not reject changes out of hand - you simple need to be aware that they’re happening. Otherwise you’ll be in hot water.
  • Communicate: great project teams have a distinct personality. Generate a brand.
  • DRY: some teams appoint a member as the project librarian.
  • Orthogonality: the closer to the user you’re allowed, to more senior you are. Programmes who are two or three levels from the actual user are unlikely to be aware of the context in which their work will be used.

TIP 60: Organize Around Functionality, Not Job Functions

  • Divide your people into small teams, each responsible for a particular functional aspect of the final system. Let the teams organize themselves internally.
  • Cohesive, largely self-contained teams of people - exactly the same criteria we should be using when we modularize code. If the user suddenly decides to change DB vendors, only the DB team should be affected.
  • This all required responsible developers and strong project management. Project needs at least two “heads” - one technical, one administrative. The technical one sets the development philosophy and style, assigns responsibilities to teams and arbitrates inevitable “discussions” between people. The technical head also constantly looks at the big picture, trying to reduce orthogonality of the common effort. The PM, schedules resources the team needs, monitors and reports the progress and helps decide priorities in terms of business needs. The administrative head acts also as the team’s ambassador when communicating with the outside world.
  • Larger projects might also need a librarian who indexes and stores code and docs and a tool builder (devops) - ensure that things get automated.
  • Know When to Stop Adding Paint: give just enough structure and support and ensure that the project delivers against the requirements.

Ubiquitous Automation

Civilization advances by extending the number of important operations we can perform without thinking. – Alfred North Whitehead

  • We want to ensure consistency and repeatability on the project. Manual procedures leave consistency up to chance

All on Automatic

TIP 61: Don’t Use Manual Procedures

  • Cron, Makefile, Build automation (CI/CD)

Automatic Administrivia

  • memory is the second thing you lose as you age. Our goal is to maintain, unattended content-driven workflow.
  • Any information generated by the nightly build should be accessible on the development Web site.
  • Look at your habits throughout the workday. Do you see any repetitive tasks. Do you type the same sequence of commands over and over again?

Ruthless Testing

  • We are driven to find out bugs now, so we don’t have to endure the shame of others finding out bugs later

TIP 62: Test Early. Test Often. Test Automatically.

  • Start testing as soon as you have code.
  • Tests that run with every build are much more effective than test plans that sit on a shelf.
  • The earlier a bug is found ,the cheaper it is to remedy. “Code a little, test a little”.
  • A good project might have well more test code than production code.

TIP 63: Coding Ain’t Done ‘Til All the Tests Run

  • code is never really done. More importantly, you can’t claim that it is usable by anyone until it passes all the available tests.
  • With good contracts in place and well tested, any integration issues can be detected easily. Otherwise, integration becomes a fertile breeding ground for bugs. In fact, it is often the single largest source of bugs in the system.
  • Failure to meet usability criteria is as big a bug as dividing by zero. You need to perform usability testing as soon as you can, while there is still time to make corrections.

Design/Methodology Testing

  • Lines of code
  • McCabe Cyclomatic Complexity Metric (measures complexity of decision structures)
  • Inheritance fan-in (number of base classes) and fan-out (number of derived modules using this one as a parent)
  • Response set (see Decoupling and the Law of Demeter). One of the many advantages of writing decoupled code is more modular testing.
  • Class coupling ratios

TIP 64: Use Saboteurs to Test Your Testing

  • After you’ve written a test to detect a particular bug, cause the bug deliberately and make sure the test complains.

TIP 65: Test Sate Coverage, Not Code Coverage

  • Even with good code coverage, the data you use for testing still has a huge impact, and more importantly, the order in which you traverse code might have the largest inpact of all.

Tightening the Net

  • If a bug slips through the net of existing tests, you need to a add a new test to trap it next time

TIP 66: Find Bugs Once

  • Once a human tester finds a bug, it should be the last time a human tester finds that bug.

It’s All Writing

The palest ink is better than the best memory. – Chinese Proverb

  • embrace documentation as an integral part of the overal development process.

TIP 67: Treat English as Just Another Programming Language

  • All documentation is a mirror of the code. If there is a discrepancy, the code is what matters.

TIP 68: Build Documentation In, Don’t Bolt it On

Comments in Code

  • too many comments are as just bad as too few.
  • Comments should explain why something is done, its purpose and its goal. The code already show the how - DRY. Document anything that is not obvious.
  • Remember that you and others after you will be reading the code many hundreds times, but writing it only a few times. Take the time to spell out connectionPool instead of cp.
  • Names are deeply meaningful to your brain, and misleading names can add chaos to your code.
  • One of the most important pieces of information that should appear in the source file is the author’s name - not necessarily who edited the file last, but the owner.
  • Documentation of any form is just a snapshot. Try to produce all documentation in a form that can be published online, on the Web, complete with hyperlinks. Put a date stamp or version number on each Web page.
  • Documentation and code are two different views of the same underlying model, but the view is all that should be different.
  • Sometimes it is uncomfortable to document the design of source code because the design isn’t clear in your mind; it’s still evolving. You don’t feel that you should waste effort describing what something does until it actually does. Does this should like programming by coincidence?

Great Expectations

  • the success of a project is measured by how well it meets the expectations of its users.

TIP 69: Gently Exceed Your Users’ Expectations

  • As your understanding of their needs develops, you’ll find areas where their expectations cannot be met, or where their expectations are perhaps too conservative. Parto f your role is to communicate this.
  • Some easy things that make users happy:
    • Tooltips
    • Keyboard shortcuts
    • Quick reference guide as a supplement to user’s manual
    • Colorization
    • A splash screen customized for the organization of the customer
    • Ability to run multiple versions of the system for training

Pride and Prejudice

  • Don’t shirk from responsibility. Instead , we rejoice in accepting challenges and making our expertise well known. If we are responsible for a design, or a piece of code, we can do a job we can be proud of.

TIP 70: Sign Your Work

  • Anonymity, especially on large projects, can provide a breeding ground for sloppiness, mistakes, sloth and bad code.
  • While the code must be owned, it doesn’t have to be owned by an individual.
  • People should see your name on a piece of code and expect it to be solid, well written, tested, and documented