Programming Practices

php|architect's Guide to PHP Design Patterns - A Practical Approach to Design Patterns for the PHP 4 and PHP 5 Developers - Jason E. Sweat.

Keep it simple

With good testing comes great freedom. At first, that 'motto' might strike you as counter-intuitive. If anything, you might assert, testing seems to be an impediment to freedom. To the contrary, if you can run tests that completely exercise your software's public interface, you can change the internals of your implementation without changing (or worse, breaking) existing applications. Testing validates the veracity and accuracy of your published interface, letting you readily change the inner workings of your code with complete confidence that it remains accurate and bug free — that you have not introduced new bugs or reintroduced old bugs.

Benefits of testing:

  1. Testing forces you to write code that is easily testable. This leads to looser coupling, flexible designs, and good modularity.
  2. Writing tests forces you to explicitly clarify your expectations of how your code is to behave, distilling your design into sharper focus from the beginning. Writing tests forces you to consider the universe of possible inputs and corresponding results.
  3. Tests are very explicitly way of communicating the intent of your code. In other words, test cases act as examples and documentation, showing exactly how a given class, method, or function should behave. A test case defines how code works in a non-ambiguous way.
  4. Automated tests are a cornerstone of many agile development. The availability of automated, self-checking tests allows developers to be much bolder in how they modify existing software. They allow a more evolutionary form of software development that support incremental delivery of functionality to the customer (motto: Deliver early; deliver often!) that speeds up user feedback and improves the quality.

Test Driven Development (TDD), also referred to as Test First Coding, is a methodology that takes testing one step further: you write your tests before you ever write any code.

Test infected. You may feel that testing is cumbersome at first, but after you begin to build an extensive test suite for your software, you'll begin to have more confidence in all of your code.

Refactoring. Even the most thoughtful and skilled programmer cannot anticipate every nuance and subtlety of a software project. Problems crop up unexpectedly, requirements can and do change, and as a result, code is refined, shared, and obsoleted. Refactoring is the practice of examining all of your code, looking for commonalities and similarities that can be unified and simplified to make code easier to maintain and extend. Refactoring also includes recognizing when a design pattern can be applied to a problem — again to make solutions simpler. Refactoring can be as simple as renaming an attribute or method, or can be as complex as collapsing an existing class. Changing your code to make it match one or more design patterns is another kind of refactoring.

The first step in refactoring is to have adequate test coverage for your existing code. This ensure that your modified code does not produce different results from your original code. Unless you change a requirement, or find a bug in a test case, your tests should not change.

Give each SQL statement a name. Putting each SQL statement into its owned, separated, and named function make it easier to understand.

Function name should be concise / meaningful. The function name should reflect what it does. A function should have a single purpose. Anytime that we allow a function to have more than one purpose (should be rare), or side effect, the function's name should also reflect that. The function name serve as documentation.

Two modes of developments. When coding, you should be in one of two modes: adding features or refactoring. When adding features, write tests and add code. When refactoring, change only existing code, making sure all that all relevant tests still run correctly. The first step in refactoring is to have adequate test coverage for your existing code.

Steps in refactoring:

  1. Identify the code that need to be refactored.
  2. Have test coverage for existing code.
  3. Work in small steps.
  4. Run your tests after each step. Code and test in quick iterations.
  5. Use refactoring to make your code more readable and to improve performance.

Class diagram describes one or more classes and how the classes related to each other in your program. Each class is represented by a box with up to three divisions: the first division is the name of the class; the second division enumerates the class attributes (class variables and instance variables); and the last division lists the class's methods. The visibility of attributes and methods are designated with + for public, - for private, and # for protected.

Sequence diagram illustrates the typical interaction of objects in the code for a particular task or event. A sequence diagram conveys when different methods are called, by whom, and in what order, and are incredibly useful instruments to communicate interactions between sets of objects to other developers.

Source Code Documentation. Of course we should document our code. Tools such as phpDocumentor extracts documentation from inside the code and generate API documentation. Advantages for using phpDocumentor (or other tools that generate documentations):

  1. Easy navigation. The resulted documents are web-based, with linked to referenced materials.
  2. You can read the documentation without being distracted by implementation. Often, you just want to know how to use a class method / function, and you don't care how it is implemented.

Run all the tests before every check-in

Guidelines for test first design:

  1. The name of the test should describe the requirement of the code
  2. There should be at least one test for each requirement of the code. Each possible path through of the code is a different requirement.
  3. Only write the simplest possible code to get the test to pass, if you know this code to be incomplete, write another test that demonstrates what else the code needs to do.
  4. A test should be similar to sample code, in that it should be clear to someone unfamiliar with the code as to how the code is intended to be used.
  5. If a test seems too large, see if you can break it down into smaller tests.
  6. If you seem to be writing a lot of code for one little test, see if there are other related tests you could write first, that would not require as much code.
  7. Test the goal of the code, not the implementation.
  8. One test/code/simplify cycle at a time. Do not write a bunch of tests, and try to get them working all at once.
  9. Keep writing tests that could show if your code is broken, until you run out of things that could possibly break.
  10. When choosing an implementation, be sure to choose the simplest implementation that could possibly work.
  11. If you are unsure about a piece of code, add a test you think might break it
  12. A test is one specific case, for which there is a known answer.
  13. If all of the tests succeed, but the program doesn't work, add a test.
  14. Tests should be as small as possible, before testing a requirement that depends on multiple things working, write a test for each thing it depends.
  15. Tests should not take longer than a day to get working, typical test/code/simplify cycles take around 10 minutes.
  16. Do not fix a bug until you have written a test that demonstrates the bug.

The Test-Code-Simplify cycle:

  1. Write a single test
  2. Compile it. It shouldn't compile, because you haven't written the implementation code it calls.
  3. Implement just enough code to get the test to compile.
  4. Run the test and see it fail.
  5. Implement just enough code to get the test to pass.
  6. Run the test and see it pass.
  7. Refactor for clarity and "once and only once"
  8. Repeat.

The reason you write code is to get a test to succeed, and you should only write the minimal code to do so. In addition to documenting how code should be used, test-first-design helps you keep the design simple right from the start, and keeps the design easy to change.

Use the right tool for the job. C++ is more dangerous than Java. Java performs type safety, but it comes as a cost (performance). C++ is probably more applicable if you are in an embedded environment (has a small amount of memory). C++ is probably more applicable if performance is critical. If you are considering to use C++, make sure that you removed all bottlenecks (network, disk IO), gave Java a chance. Use C++ only if you need to. If you are writing a script, use Perl, bash, etc.. If you are writing a complex program, use Java or C++.

Code Smell:

  1. Duplicated Code
  2. Method too big
  3. Classes with too many instance variables
  4. Classes with too much code

The Waterfall Methodology:

The waterfall model is a sequential software development model, a process for creating software, in which development is seen as flowing steadily downwards (like a waterfall) through the phases of requirements analysis, design, implementation, testing (validation), integration, and maintenance.

To follow the waterfall model, one proceeds from one phase to the next in a purely sequential manner. For example, one first completes requirements specification, which are set in stone. When the requirements are fully completed, one proceeds to design. The software in question is designed and a blueprint is drawn for implementers (coders) to follow — this design should be a plan for implementing the requirements given. When the design is fully completed, an implementation of that design is made by coders. Towards the later stages of this implementation phase, disparate software components produced are combined to introduce new functionality and remove bugs.

Thus the waterfall model maintains that one should move to a phase only when its preceding phase is completed and perfected.

The Agile Methodology:

Release early, release often. Frequent (potentially ship-able) internal releases.

Twelve Principles of Agile Software

Agile builds upon the success of other methodologies (Test First Development, Pair Programming, Extreme Programming).

The catch line for Agile software development is develop quickly, deliver often. There are many agile development methods; most minimize risk by developing software in multiple repetitions (or 'iterations') of short time frames (known as 'timeboxes'). Software developed during one unit of time is referred to as an iteration, which typically lasts from two to four weeks. Each iteration passes through a full software development cycle, including planning, requirements analysis, design, writing unit tests, then coding until the unit tests pass and a working product is finally demonstrated to stakeholders. Documentation is no different from software design and coding. It, too, is produced as required by stakeholders. An iteration may not add enough functionality to warrant releasing the product to market, but the goal is to have an available release (with minimal bugs) at the end of each iteration. At the end of each iteration, stakeholders re-evaluate project priorities with a view to optimizing their return on investment.

Agile teams:

  • Product owner
  • Cross-functional team
  • Scrum-master
  • External roles

Agile practices:

  • Sprint planning
  • Retrospective

What we may want to do:

  • Lunch meeting to size the backlog.
  • Lookahead Sizing. Rather than just having a separate meeting for sizing the backlog, the team, as part of current iteration, should be informed of what features will be likely be in the next iteration, that way they can do sizing for the next iteration. I often came to the sizing meeting without knowing what will be work on. Features that are complex, and requires a lot of thinking, can't fit into one hour meeting. Even if you have multiple developers on your team, therefore multiple ideas, these ideas are often not quite thought out. When the ScrumMaster send email invitation to the sizing meeting, I suppose that I can look at the backlog, and try to size it, but sizing a complex feature often requires a team effort and discussion, therefore better be done throughout an iteration, rather than in one hour meeting.

More on Agile

Brooks’ Law: "Adding manpower to a late software project makes it later."
Leap in software is not governed by Moore’s Law; it’s governed by Murphy’s Law
Moore's Law: hardware power typically double every x year
Murphy Law: is an adage or epigram that is typically stated as: "Anything that can go wrong, will go wrong".
Second system effect: Take a team of programmers who’ve done something brilliant. Give them a blank slate for the next project. The result? They fail…hard. Why? They become overconfident. Their earlier success causes them to overreach.
Software crisis
"A project become a year late one day at a time" (in The Mythical Man-Month
Literate Programming
Incrementalism. The basic idea: "We’ll see you in two weeks" vs. "See you in two years"

Movies about project management:
Mr. Blandings Builds His Dream House
The money pit
Are We Done Yet?

Microview: good coding conventions
Macroview: good architecture
Architecture is hard, but conventions is easy

You should think of programs as a medium of intentional communication. Communicating detailed instructions with the machine. Communicating with your development community. Communicating with yourself (and especially your future self).

Good architecture is about not collapsing into a puddle of confusion, and should enable you to change a correct program into another correct program

Working Together In "War Rooms" Doubles Teams' Productivity, University Of Michigan Researchers Find
The History of Lean Software Development
Extreme Programming (Wikipedia)
Extreme Programming: A gentle introduction
Extreme Programming Rules
Extreme Programming vs. Interaction Design
Extreme programming explained: embrace change By Kent Beck (this is a book, product management should not be required to read this book)
Extreme programming installed By Ron Jeffries, Ann Anderson, Chet Hendrickson (another book on XP)
What is Extreme Programming
Beyond Agile — Inspect and Adapt? How?
Beyond Agile — The Agile Barrier
5-Steps to Project Success (Seriously!) v1.1
Software Development Methodology
Exposing the Fallacy of "Good Enough" Software
Agile Retrospectives
Scrum at Google
Mary Poppendeick at Google
Ken Schwaber at Google
Mike Cohn at Google Part 1
Agile Testing
Using open source tools for performance testing
Using Fit: An Open-Source Testing Framework
Building Testable AJAX Applications
Literate Functional Testing
Push Button Testing using Contracts
Distributed Continuous Quality Assurance
Leveraging Unit Tests As Functional Tests, Load Tests, and Service Monitors
Customer Centric Web Decision Making
Using Static Analysis For Software Defect Detection
Becoming a Software Testing Expert
Extending Selenium
Selenium: In-browser Acceptance Testing Tool
Building Framework Around Selenium
Listening to Code Smells (unit tests)
The Paradox of Choice - Why More Is Less
How to Design Good API's
Core Patterns for Web Permissions
Agile Restrospective
Scrum at Google
Best Practices for Agile/Lean Documentation
Mary Poppendeick at Google
Ken Schwaber at Google
Mike Cohn at Google Part 1
Agile Testing
Using open source tools for performance testing
Building Testable AJAX Applications
Literate Functional Testing
Push Button Testing using Contracts
Distributed Continuous Quality Assurance
Leveraging Unit Tests As Functional Tests, Load Tests, and Service Monitors
Customer Centric Web Decision Making
Using Static Analysis For Software Defect Detection
Becoming a Software Testing Expert
Listening to Code Smells (unit tests)
The Paradox of Choice - Why More Is Less
How to Design Good API's
Core Patterns for Web Permissions
Pair Programming

CUSEC 2010 Keynote: Douglas Crockford – The Software Crisis
Global variables are evil

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License