The second chapter of the book is about the Pragmatic Approach. Here is a small sumary of its points:

Duplication is bad

Really bad. Use the DRY principle. The duplication can come in diferent flavours:

IMPOSED DUPLICATION. It seems you have no choice; YOU HAVE. If you feel like you don't probably need a to review the design of your application. You can have multiple representations of the data like documentation:

Programmers are taught to comment their code: good code has lots of
comments. Unfortunately, they are never taught WHY code needs comments: bad
code REQUIRES lots of comments.

If we comment everything eventually we'll make a change on the code that will not be reflected on the comments, and untrustworthy comments are worse than no comments at all.

Notice that comments and documentation are NOT the same. You need to document in a higher level how the code is structured, how you are supposed to use it and maybe give some examples of some common use cases.

While you can still have the same problem when documenting - because the duplication - you can try to automate things like using the unit testing of your project as examples or test also the examples of the documentation to see if they are still valid.

INADVERTENT DUPLICATION. Sometimes it comes as mistakes of bad design and you duplicate data structures where the information can get inconsistent.

IMPATIENT DUPLICATION. Copy-paste, make a few changes and we're done. Before doing this lazy thing think about the Y2K fiasco a little bit.

INTERDEVELOPER DUPLICATION. Different developers are working and implementing the same things. A way to solve it? Make code easy to reuse. Have a central place of code where people can easily find it. Document it; don't put barriers to other developers to reuse your work.

Ortogonallity

This concept, for me, is easy to understand and difficult to apply when programming. The idea is to eliminate effects between unrelated things. Not creating dependencies between software modules is key for that. Use APIs and pass always serialized data between interfaces. Don't pass state or objects used by other submodules. The idea is: how other subsystems could be broken if I change this function of this module. If the answer is 'a lot' or 'I don't know', you are doing it wrong. The idea is that each module can be tested by itself, it promotes reuse, its easy to do code refactoring and reduces risks.

Reversibility

Nothing is more dangerous than an idea if it's the only one you have.

The main idea is that while you have to follow a specification for your code, you don't want to make decisions that will not be reversible in the future. We don't usually make the best decisions the first time and is a problem if you have to be married forever with that decision. Try to not make final decisions.

If there is something that can be changed in the future, try to hide it behind an abstraction (ORM/REST API/Module/...). This way you can change the implementation or the technologies below it without any major impact.

Tracer bullets

The concept behind the idea is to make a really straightforward change and get immediate feedback about it, being able to adjust what you are trying to do immediately and get feedback again. The opposite is to take long requirements, develop during months in something you don't know if is going to be used.

Tracer code is not meant to be disposable; is not a prototype. Instead, is something you have started but is not yet fully functional. Adding functionality is easy, and the propose is to not develop stuff you're not going to use.

Prototypes

Here it comes the disposable code, the mock-ups that don't have any logic behind them, the API that doesn't do any validation whatsoever, new architecture, new integration with a third party tool.

The prototype is a learning experience. Its value lies not in the code produced, but
in the lessons learned. That's really the point of prototyping.

Domain languages

Here the author of the Pragmatic Programmer suggest us to create a simple language to express requirements, for example. This way the client can give you in a very precise way what he needs. As they say: "Program Close to the Problem Domain". Problem being that I've never created a language (or mini-language) using Lex and Yacc, for example and this section is very difficult for me to understand or find a good example where this would be worth it.

Estimating

Estimate to Avoid Surprises

A few notes about estimating: try to give the units of your estimation in a meaningful unit. Is not the same '60 days' than 'about 2 months'. The first is much more precise while the second seems like a guess that can vary quite a lot.

To give a good estimation is really important to know what is being asked. Build the model of the System and break the model into Components. Then rate what is going to take each component.

One friend of mine, LB, told me once a good way to estimating things:

Think what is going to take to do all the parts of a project. Sum them up.
Add 20% at that result. Multiply by 2.

In your head everything is easy. You don't think about the small problems
you don't know yet you are going to have.

I must admit that all the times that I've applied this rule I have given a fairly good estimation and I try to always follow it.