428x Filetype PDF File size 0.11 MB Source: www.inf.fu-berlin.de
7. "Every piece of knowledge must have a
The essence of single, unambiguous, authoritative
representation within a system.", or shorter:
"Pragmatic Don't repeat yourself (DRY).
Some duplication may seem unavoidable (e.g.
Programmer" because the same thing must be expressed in
more than one notation), sometimes it
happens because of inattentiveness or
A heavily paraphrased summary of the book laziness of one developer or because several
people introduce the same knowledge
Andrew Hunt, David Thomas: The Pragmatic independently, but all of these types can be
Programmer: From Journeyman to Master, avoided: e.g. by code generators, by
Addison-Wesley Professional 1999 (321 recognizing that duplication will usually
pages) become costly later on, by making modules
(Lutz Prechelt, 2013) easy to reuse, and by a sound software
architecture and strong communication.
8. Orthogonality: Eliminate effects of one
Ch. 1: A Pragmatic Philosophy thing onto an unrelated thing. This will make
"Pragmatic Programmers […] think beyond changes local, make parts testable
the immediate problem, always trying to individually and more reusable, make the
place it in its larger context, always trying to overall system more robust. Even team
be aware of the bigger picture." They think members can be more or less orthogonal to
critically. each other. Also, avoid relying on things you
cannot control. Use refactoring to constantly
They take responsibility for everything they move towards more nearly orthogonal code.
do, refuse doing it if they cannot, and have Testing and bugfixing provide good indicators
no fear of appearing weak or incompetent. of successful orthogonality: Can you easily
If something goes wrong, they act write tests that test only a single module?
constructively and offer options, not excuses. Does fixing a bug usually involve a single file
When they see how things ought to be, they only? Then the system's orthogonality is high.
work as catalysts to make it happen (e.g. 9. Reversibility: There are no final decisions.
using the stone soup trick). Make sure that reversing a decision is cheap.
They understand the context in which they Introducing suitable abstractions goes a long
work and so understand what is sufficient: way for that.
What makes good-enough software. They 10. Tracer Bullets: Write your system in such
explicitly trigger requirements discussion on a way that your code helps to find out quickly
quality levels. and easily how close to the target you are –
But they will immediately repair (or at least and (unlike prototyping) supports getting
board up) any "broken window" they find: closer. The actual solution must be
anything in the software that is not clear and operational long before it is fully functional:
orderly. to provide something to show the users, to
They keep learning explicitly all the time and provide an architecture and integration
also continually strive to become better at platform for the developers, and to provide a
communicating and understanding their definite measure of progress.
audience. They treat learning like financial 11. Prototypes: Prototypes are vehicles for
investment, with notions of diversification, understanding a few particular aspects of a
making both low-risk and high-risk system, e.g. of visual design, a workflow, a
investments, buying low and selling high, and critical performance issue, or the behavior of
rebalancing the portfolio. some technology. They are built in the
cheapest possible manner (which needs not
involve program code) and are thrown away
Ch. 2: A Pragmatic Approach after the question is solved, because they are
There are some ideas how to approach about the lesson learned only. Make sure
software development that apply at many everybody involved understands they will be
different levels and in any software thrown away.
development domain: 12. Domain-specific languages: Program close
to the application domain. Use domain
vocabulary at least. If domain experts use Use it for all development (even throw-away
unambiguous language, emulate its stuff) and version all relevant files, not only
semantics and perhaps also its syntax (either source code.
for specification only or even in executable 18. Debugging: Debugging is just problem
fashion). Consider different mini-languages solving; treat it as such.
for different types of users. There can be • Focus on the problem, not on blaming.
mere data languages (often using rather • Don't panic. Think.
simple formats), executable languages, or • Fix the cause of the failure, not its
metaprogramming (generating or symptoms.
manipulating parts of the application). • Fix any compiler warnings first. Reproduce
13. Estimating: Make it a habit to estimate the failure then (get help if you cannot)
how large things are going to be: Memory and automate the reproduction.
requirements, disk space requirements, • Preferably use a good debugger program
bandwidth requirements, run times, with data visualization capabilities.
development times, event frequencies (both • Use binary search to narrow problems
at run time and in the development process) down.
and so on. This avoids surprises. • Use tracing/logging where the debugger
Consider the accuracy required and use does not work well and use or make
suitable units. Draw on the experience of software that helps to wade through the
others if possible. Make assumptions explicit. tracing output.
Build models. If the estimate is difficult but • Explain partial insights to someone else to
important, produce multiple estimates with complete them.
different approaches. For project estimates, • Suspect your project's code first, not
that very same project can be a source of compilers or external libraries, but
estimation knowledge if incremental changes to those (or the OS) may break
development is used. When asked for an your code without you doing anything.
estimate, answer "I'll get back to you" and • Don't assume, check.
take your time. • Once you found the problem, add the test
that would have caught it and look for
further similar problems. If the failure
Ch. 3: The Basic Tools happens far away from the defect, add
Every craftsman needs high-quality tools. more integrity checks to the code.
Their skilled use improves only over time but • If the defect was due to a
you should still constantly look for better misunderstanding, make sure to clear that
tools, too. up with the author and find a way to avoid
similar misunderstandings in the future.
14. Plain text: Our material is knowledge, its • If debugging took long, reflect why.
best representation is human-understandable 19. Text manipulation: Learn Python, Ruby,
structured or semi-structured plain text, or Perl for sifting through and processing
because that is best for analysis and plain text. Use it for automating many things.
manipulation (except sometimes when small 20. Code generators: Write code that writes
size and high processing speed of binary code, either for subsequent manual editing
format prevails), is most interoperable, does (passive generators: for convenience; the
not become obsolete, and is best supported code needs not be complete or perfect) or for
by tools. immediate use (active generators: for
15. Shell: The shell is to a programmer what following the DRY principle).
a work bench is to a woodworker: The center
of work. GUI tools are just too inflexible to
make the shell obsolete. On MS Windows, use Ch. 4: Pragmatic Paranoia
Cygwin.
16. Editor: Know one editor really well; it You cannot write perfect software. Therefore,
should be multi-platform, extensible, and do not waste energy trying; be pragmatic.
programmable. Code defensively: Don't trust the code and
17. Version control: Is required to undo data of others -- nor your own!
multiple days of changes when needed, to 21. Design by contract: Specify preconditions
find out who changed what when, to measure and (simplified) postconditions explicitly.
the amount of change over time, to find Perform run-time checking for them, using
hotspots of change, to automate builds, etc. assert if you have nothing else or using a
stronger mechanism (that includes business data. Great applications can change
inheritance and object invariants) if available them even without requiring a restart.
for your language. Preprocessors tend to be 28. Temporal coupling: Design for maximal
messy. concurrency, avoid introduce unneeded
22. Crash early: Check many things that ordering constraints on steps. This may help
"cannot happen" or absolutely must not your design quality, too, e.g. because you
happen and crash the program if they may ask yourself why that global variable
happen. that you now need to lock exists at all.
23. Assertions: Check many things that 29. Events and views: Event-based control is
"cannot happen" or absolutely must not a good decoupling mechanism, e.g. in a
happen and do not be intimidated by the publish/subscribe (observer) structure. In
runtime overhead prematurely. Turn off only particular, separate views from models, e.g.
those assertions that are really too slow. in a model-view-controller (MVC) structure,
Make very sure your assertions have no side whether in the context of GUIs or elsewhere.
effects. You can stack them: One structure's view
24. Exceptions: Use exceptions to free the becomes the next-higher structure's model.
code from too much intermingled problem 30. Blackboards: An even stronger form of
handling in order to make the main execution decoupling, where only data structures (or
path clearly visible. Use exceptions for objects) are shared but no call coupling is
unwanted conditions that are at least explicit is a blackboard storage (tuple space,
somewhat surprising, not for fully regular e.g. JavaSpace), asynchronous and possibly
ones. transactional, were events are created by the
25. Resource management: Whenever you fact that an object with certain properties
can, "finish what you start", i.e. the routine appears in the storage (written by some other
that allocates a resource should be participant). Often combined with rules
responsible for deallocating it. Deallocate in engines to coordinate workflows.
the opposite order of allocation. Use standard
allocation orders to avoid deadlock. Wrap Ch. 6: While You Are Coding
resource use in classes so the destructor can
clean up left-over resources. In Java, finally 31. Programming by coincidence: To make
is your friend for cleaning up reliably. Where sure your program works tomorrow, you must
"finish what you start" is not applicable, the thoroughly understand why it works today. If
resource should become part of some you don't, your code may be slow, confusing,
container that is responsible for deallocation. only partially correct, error-prone to change,
and prone to collapse if the objects change
Ch. 5: Bend or Break that it is calling. Know what you are relying
on. Don't rely on anything you need not rely
The world changes constantly, so code must on.
be flexible, too. 32: Algorithm speed: O-Notation and runtime
26. Decoupling and the Law of Demeter: complexity classes. Complexity estimation
Couple your classes to no more other classes rules-of-thumb. Estimate. Then test your
than reasonably necessary: your instance estimates. Be pragmatic about algorithm
variables, method arguments, and new local choice.
objects (the Law of Demeter). Have those 33. Refactoring: Building SW is more like
objects perform a complete service for you gardening than like construction; a constant
rather than giving you an object with which process of monitoring and care. Regularly
you can perform the service. This will require refactor your SW to push back duplication,
many delegation-only methods, though. non-orthogonal design, outdated knowledge,
27. Metaprogramming: Provide many and performance degradation. Refactor early,
configuration option to avoid change refactor often – and avoid telling your clients
programming. Put abstractions in code, you do it. Make small steps and do not
details in metadata. If you drive this far change functionality at the same time. Have
enough, you may even be able to implement automated tests to safeguard your changes.
different systems using the same application 34. Code that's easy to test: Write automated
engine, just with different metadata. Business tests for each module that test against its
rules and workflows are good candidates for contract (self-testing code). Use assertions in
the code. Test lowest-level modules first and
higher-level modules later (to simplify defect requirements specification notations your end
localization). Co-design code and its tests. users do not understand, beware of developer
Store the test code close to the module code. overspecialization, beware of methods that
Tests also serve as documentation. Use a test restrict the flexibility of your designs (e.g.
harness. Do not throw away the ad-hoc tests regarding the use of metadata for configuring
you invented during debugging. Make sure behaviors). Never underestimate the learning
you can test your software during production, cost for a new method. Each method should
too. Log files and semi-official debugging be a tool in your toolbox, selected and used
console windows or built-in webservers are when appropriate and its use constantly
helpful. Establish a standardized test culture refined.
(as e.g. on the Perl platform).
35. Evil wizards: If you use a Code Ch. 8: Pragmatic Projects
Generation Wizard, make sure you
understand the code produced, because it will 41. Pragmatic teams: All the above advice
be interwoven with your application. applies even more strongly at the team level.
Teams must not accept broken windows,
Ch. 7: Before the Project must constantly look out for deteriorating
conditions, avoid duplication. A team should
36. The requirements pit: "Requirements create a brand for itself and communicate
rarely lie on the surface. Normally, they’re consistently to the outside. Appoint topic
buried deep beneath layers of assumptions, experts. Use groupware. Communicate and
misconceptions,and politics." Identify policies discuss lively within the team. Organize
(e.g. access privilege rules) that come as part around functionality, not job roles. Isolate
of requirements and expect them to be sub-teams from each other by design by
volatile; make them configurable. Identify contract, Law of Demeter, orthogonality. Even
user interface details that come as part of good teams need a technical head and an
requirements and initially treat them as administrative head, larger ones also a
manner of speaking only. Understand and librarian and a tool builder.
document why users want certain things, not 42. Ubiquitous automation: Avoid manual
just what. To understand the domain, become procedures. Automation is more efficient,
a user yourself for a week – it helps build more consistent, and more accurate. Use
trust and rapport, too. To document scripts (such as buildfiles) to automate
requirements, use a suitable Use Case routines and cron to automate even their
format. Do not overspecify, stick to what's occurrence. Apply this even to tasks with
strictly needed. Track all requirements manual aspects in them, e.g. by manually
changes to avoid creeping featurism. Maintain annotating code with "needs review" and then
a glossary and stick to those terms. Use automating the review management only.
hypertext and internally publish the 43. Ruthless testing: Test early, test often,
requirements. test automatically. Use unit tests to catch
37. Solving impossible puzzles: The key to local defects and integration tests to catch
coping with seemingly impossible problems is non-local ones. A good project may have
discriminating real constraints from perceived more test code than production code. Besides
ones. Are you even solving the right problem? functional testing there are requirements
Then, enumerate all conceivable (not: validation tests(*), error-and-recovery tests,
possible) routes and carefully explain for each performance tests, load tests, usability
why it cannot work. Find your weak tests(*), and others. Except for (*), they are
arguments: There are your possibilities. automatable: automate them. Use artificial as
38. Not until you're ready: Don't start as long well as real data. Avoid testing at the GUI
as you have doubts, but start when you are level much. Test your tests by planting
ready. How to discriminate real doubts from defects intentionally and see how many are
mere procrastination? Prototyping will often caught. Assess your tests' coverage,
reveal the problem behind your doubts or preferably state coverage, not just code
quickly get you to readiness. coverage. Add an automated test for it when
39. The specification trap: Don't write highly you manually found a defect.
detailed specifications. 44. It's all writing: Treat documentation as an
40. Circles and arrows: Don't become a slave integral part of your project. Apply all the
to formalized methods. Beware of other principles: Treat English as just another
programming language, avoid duplication,
no reviews yet
Please Login to review.