289x 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.