More on this book
Community
Kindle Notes & Highlights
The scope and quality of the system you produce should be specified as part of that system's requirements.
many users would rather use software with some rough edges today than wait a year for the multimedia version.
If you give your users something to play with early, their feedback will often lead you to a better eventual solution
Once you feel comfortable with some new language or bit of technology, move on. Learn another one.
resume. The process of learning will expand your thinking, opening you to new possibilities and new ways of doing things.
Plan what you want to say. Write an outline. Then ask yourself, "Does this get across whatever I'm trying to say?" Refine it until it does.
The word annoy comes from the Old French enui, which also means "to bore."
Make what you're saying relevant in time, as well as in content. Sometimes all it takes is the simple question "Is this a good time to talk about...?"
maintenance is not a discrete activity, but a routine part of the entire development process.
Structures in multiple languages can be built from a common metadata representation using a simple code generator each time the software is built (an
keep the low-level knowledge in the code, where it belongs, and reserve the comments for other, high-level explanations.
untrustworthy comments are worse than no comments at all.
Where possible, always use accessor functions to read and write the attributes of objects.
If components have specific, well-defined responsibilities, they can be combined with new components in ways that were not envisioned by their original implementors.
You get more functionality per unit effort by combining orthogonal components.
With DRY, you're looking to minimize duplication within a system, whereas with orthogonality you reduce the interdependency among the system's components. It
often the only way to determine the timetable for a project is by gaining experience on that same project.
the team, their productivity, and the environment will determine the schedule.
They do not need to say a word; the simple act of explaining, step by step, what the code is supposed to do often causes the problem to leap off the screen and announce itself.[7]
"lazy" code: be strict in what you will accept before you begin, and promise as little as possible in return.
Developers who are afraid to change code because they aren't sure what might be affected
(a response set is defined to be the number of functions directly invoked by methods of the class).
Because following the Law of Demeter reduces the size of the response set in the calling class, it follows that classes designed in this way will also tend to have fewer errors
No amount of genius can overcome a preoccupation with detail
program for the general case, and put the specifics somewhere else—outside the compiled code base.
Metadata can be expressed in a manner that's much closer to the problem domain than a general-purpose programming language might be
capture their description of workflow using a notation such as the UML activity diagram.[4]
Make code easy to test, and you'll increase the likelihood that it will actually get tested,
Don't just test your code, but test your assumptions as well. Don't guess; actually try it. Write an assertion to test your assumptions
don't let what you've already done constrain what you do next—be ready to refactor
When we write that a particular sort routine sorts n records in O(n2) time, we are simply saying that the worst-case time taken will vary as the square of n.
O(n2/2+ 3n) is the same as O(n2/2), which is equivalent to O(n2).
Figure 6.1. Runtimes of various algorithms
How long will it take to process 1,000? If your code is O(1), then it will still take 1 s. If it's O(lg(n)), then you'll probably be waiting about 3 s. O(n) will show a linear increase to 10 s, while an O(n lg(n)) will take some 33 s. If you're unlucky enough to have an O(n2) routine, then sit back for 100 s while it does its stuff. And if you're using an exponential algorithm O(2n), you might want to make a cup of coffee—your routine should finish in about 10263 years.
If a simple loop runs from 1 to n, then the algorithm is likely to be O(n)
If you nest a loop inside another, then your algorithm becomes O(m x n), where m and n are the two loops' limits.
Such sorting algorithms tend to be O(n2).
If your algorithm halves the set of things it considers each time around the loop, then it is likely to be logarithmic, O(lg(n))
Algorithms that partition their input, work on the two halves independently, and then combine the result can be O(n lg(n)). The classic example is quicksort, which works by partitioning the data into two halves and recursively sorting each.
Time a combinatoric algorithm for five elements: it will take six times longer to run it for six, and 42 times longer for seven. Examples include algorithms for many of the acknowledged hard problems—the traveling salesman problem, optimally packing things into a container, partitioning a set of numbers so that each set has the same total, and so on. Often, heuristics are used to reduce the running times of these types of algorithms in particular problem domains.
If you have an algorithm that is O(n2), try to find a divide and conquer approach that will take you down to O(n lg(n)).
Time pressure is often used as an excuse for not refactoring. But this excuse just doesn't hold up: fail to refactor now, and there'll be a far greater time investment to fix the problem down the road—when there are more dependencies to reckon with.
Make sure that users of the affected code know that it is scheduled to be refactored and how this might affect them.
keep your steps small, and test after each step,
write test cases that ensure that a given unit honors its contract.
test subcomponents of a module first.
When you design a module, or even a single routine, you should design both its contract and the code to test that contract. By
At the end of the debugging session, you need to formalize the ad hoc test.
Figure 7.2. A sample use case