Showing posts with label development. Show all posts
Showing posts with label development. Show all posts

Friday, April 24, 2015

SOLID Review: Dependency Inversion Principle

Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

The final SOLID principle is known as the Dependency Inversion principle. Arguably the most important of the five principles, the Dependency Inversion principle can be thought of as a culmination of the principles preceding it. Systems that abide by the other SOLID principles tend to follow the Dependency Inversion principle as a result. The principle states:

"High-level modules should not depend on low-level modules."

A better way to think about it is:

"Abstractions should not depend upon details. Details should depend upon abstractions."

In a static-typed language like Java, "abstractions" can be implemented and enforced explicitly via interfaces. However, in a dynamic language like Ruby, we depend on duck-typing to describe an object's interface. Even without explicit interfaces in Ruby, the Dependency Inversion principle still holds value! We should still aim to depend on abstractions rather than details.

Let's look at an example! We'll revisit a simple example from my blog post on the Open/Closed Principle.

A Simple String Transformer

Suppose we have a class called Transformer that takes a string and transforms it into a some other object or value. For starters, we'll have it transform JSON strings into Ruby hashes:
require 'json'

class Transformer
  def initialize(string)
    @string = string
  end

  def transformed_string
    JSON.parse(@string)
  end
end

Transformer.new('{"foo": "bar"}').transformed_string
# { "foo" => "bar" }

Now, we'll extend the functionality of our Transformer by allowing it to transform strings into binary:
require 'json'

class Transformer
  def initialize(string)
    @string = string
  end

  def transformed_string(type)
    if type == :json
      JSON.parse(@string)
    elsif type == :binary
      @string.unpack('B*').first
    end
  end
end

Transformer.new('Hello').transformed_string(:binary)
# "0100100001100101011011000110110001101111"
Now, our Transformer takes strings and transforms them into one of two different types: a Ruby hash or its binary representation. At this point, we should notice some code-smell! The transformed_string method is very dependent on JSON.parse and String.unpack. These are implementation details that our Transformer shouldn't care about.

Let's apply the Dependency Inversion principle by making Transformer depend on an abstraction rather than coupling to concrete details!

The Transformation Abstraction

The basic functionality of our Transformer class is to transform strings into several different types of objects or values. It does this by utilizing different transformations. This seems like an abstraction we can extract and encapsulate! We'll make Transformer depend on a new abstraction called Transformation:
class Transformer
  def initialize(string)
    @string = string
  end

  def transformed_string(transformation)
    transformation.transform(string)
  end
end

class BinaryTransformation
  def self.transform(string)
    string.unpack('B*').first
  end
end

Transformer.new('Hello').transformed_string(BinaryTransformation)
# "0100100001100101011011000110110001101111"

require 'json'

class JSONTransformation
  def self.transform(string)
    JSON.parse(string)
  end
end

Transformer.new('{"foo": "bar"}').transformed_string(JSONTransformation)
# { "foo" => "bar" }
Rather than having Transformer depend on low-level implementation details (JSON.parse and String.unpack), it now depends on a single method: transform. This single method is what makes up the interface of our Transformation abstraction! Now, we can create as many Transformations as we want without modifying Transformer:
require 'digest'

class MD5Transformation
  def self.transform(string)
    Digest::MD5.hexdigest string
  end
end

Transformer.new('Hello').transformed_string(MD5Transformation)
# "8b1a9953c4611296a827abf8c47804d7"

Conclusion

As you can see, the Open/Closed principle is highly correlated with the Dependency Inversion principle! We actually end up following the Open/Closed principle by abiding by the Dependency Inversion principle. In fact, some form of dependency abstraction is often required to abide by all the other SOLID principles. If there's one principle to remember out of all the SOLID principles, it's the Dependency Inversion principle: depend on abstractions, not low-level details!

Happy coding!

Friday, March 27, 2015

SOLID Review: Interface Segregation Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

The Interface Segregation Principle is probably the most straight-forward of all the SOLID principles. It states:

"Clients should not be forced to depend on methods that they do not use."

In dynamic languages, this isn't really much of an issue because there is no way to define and force the implementation of interfaces on classes (like in Java). Instead, a set of methods determines whether or not an object implements an interface. If an object responds to "a particular set of methods", it has implemented that "particular interface".

In Ruby, modules can be used to define and share sets of methods across multiple classes. Using this construct, we can define different "interfaces". So, when we say "keep our interfaces segregated", we're really saying "keep our modules segregated". This leads to highly cohesive modules.

There are two main benefits to cohesive modules in Ruby: less coupling and more readable code.

Implementing Phones

By keeping our modules small and focused, we are simply applying the Single Responsibility Principle, but for modules. For example, let's create a module called Phone:
module Phone
  def call(number)
    "Calling #{number}..."
  end

  def hangup
    "Hanging up!"
  end

  def text(number, message)
    "Texting '#{message}' to #{number}."
  end
end
Here, we have a set of common behaviors for phones. We can make use of this by including them in our class. Let's create a CellPhone:
class CellPhone
  include Phone
end
Now, our CellPhone class implements the methods in Phone! Any instance of CellPhone can call, hangup, and text other numbers.

Let's create a new class called RotaryPhone:
class RotaryPhone
  include Phone

  # Eek... code smell.
  def text(number, message)
    raise 'Cannot text on this type of phone!'
  end
end
Since we are overriding one of the methods in our module, it's a sign our module isn't cohesive enough. Our RotaryPhone is being littered with methods it does't need!

Another issue worth noting is the tight coupling between our two classes caused by sharing the same, non-cohesive module. Suppose we don't override the text method in RotaryPhone:
class RotaryPhone
  include Phone
end
Any errors caused by text in our Phone module would end up in both classes, even though RotaryPhone doesn't care about text! This tight coupling between CellPhone and RotaryPhone is unnecessary.

Segregate the Modules

A good solution for our problem is to segregate the basic phone behaviors from the mobile phone behaviors:
module BasicPhone
  def call(number)
    "Calling #{number}..."
  end

  def hangup
    "Hanging up!"
  end
end

module MobilePhone
  def text(number, message)
    "Texting '#{message}' to #{number}."
  end
end
Now, each of our classes implement only the modules they require:
class CellPhone
  include BasicPhone
  include MobilePhone
end

class RotaryPhone
  include BasicPhone
end

A Readable, Loosely-Coupled Solution

The behaviors of each class are more clearly defined by the explicitness of the modules it includes. Also, CellPhone and RotaryPhone are only coupled by the methods in BasicPhone, which makes sense since they both require the basic behaviors or call and hangup. Both of our issues above are solved!

Conclusion

Although the Interface Segregation Principle is less important in dynamic languages like Ruby, it still leads to cohesive, readable classes. By keeping modules focused, we end up with looser coupling and cleaner "interface" definitions. They aren't major wins, but wins nonetheless!

Happy coding!

Thursday, March 5, 2015

SOLID Review: Liskov Substitution Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

Barbara Liskov introduced her substitution principle back in 1987 during her keynote titled Data Abstraction and Heirarchy. Today, it is one of the five SOLID principles in object-oriented programming. The original definition is as follows:

"Let q(x) be a property provable about objects x of type T. Then q(y) is provable for objects y of type S, where S is a subtype of T."

Simply put:

"Instances of any type should be replaceable by instances of its subtypes without creating incorrect behaviors."

How can we ensure that our classes abide by the Liskov Substitution Principle? For starters, we must ensure that any subtype implements the interface of its base type. In the world of dynamic languages, this is better stated as a subtype must respond to the same set of methods as its base type.

We must also ensure that methods in any subtype preserve the original promises of methods in its base type. What "promises" are we talking about? For that, we turn to another design principle known as Design by Contract.

Design by Contract

The concept of Design by Contract was coined by Bertrand Meyer in his book Object Oriented Software Construction. It's official description is much more detailed, but to paraphrase, there are three basic principles:
  • Subtypes should not strengthen any preconditions of its base type. That is, requirements on inputs to a subtype cannot be stricter than in the base type.
  • Subtypes should not weaken any postconditions of its base type. That is, the possible outputs from a subtype must be more than or equally restrictive as from the base class.
  • Subtypes must preserve all invariants of its base type. That is, if the base type has guarantees that certain conditions be true, its subtype should make those same guarantees.

If any of the above are violated, chances are the Liskov Substitution Principle is also violated.

A Liskov Substitution Checklist

Let's look at a simple example. We're going to model several types of birds. We'll start by defining a base type called Bird:
class Bird
  def initialize
    @flying = false
  end

  def eat(food)
    if ['worm', 'seed'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end

  def lay_egg
    # The Egg class has a method 'hatch!' that returns a new Bird.
    Egg.new 
  end

  def fly!
    @flying = true
  end
end
Instances of Bird are very simple. They eat only certain types of food, lay eggs, and can go from sitting on the ground to flying in the air. For now, ignore the fact that our Bird cannot go back on the ground. Here's a small program that uses our Bird:
bird = Bird.new

bird.eat('worm') # Ate worm!

egg = bird.lay_egg # Returns an Egg
egg.hatch! # Returns a new Bird

bird.fly! # @flying == true
Remember, any subtypes from Bird should be able to work in our program above. Now, let's create some subtypes of Bird and see how we can apply the Liskov Substitution Principle.

  • The subtype must implement the base type's interface.

In most programming languages, we can achieve this through basic inheritance. Since we already have a base class defined, we'll take this approach. However, there are many ways to achieve this across many languages. In Ruby, we can use modules to share methods (see duck-typing). In Java, we can implement interfaces.

Let's create a Pigeon subclass:
class Pigeon < Bird
end

bird = Pigeon.new # Behaves exactly like Bird!
Success! Pigeon now implements Bird's interface.

  • The subtype should not strengthen preconditions of the base type.

Let's say our Pigeons can only eat bread. We will override the eat method to achieve this:
class Pigeon < Bird
  def eat(food)
    if ['bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

# bird is now Pigeon
bird.eat('worm') # raises an error: "Does not eat worm!"
Since we've actually made the preconditions to our method stricter than in the Bird class, we've violated the Liskov Substitution Principle! In doing so, we've broken our existing program!

Instead, let's say that Pigeons can eat bread in addition to seeds and worms. Then, we've weakened the preconditions and are well within our rule:
class Pigeon < Bird
  def eat(food)
    if ['worm', 'seed', 'bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

bird.eat('worm') # "Ate worm!"
And our program works with our subclass!

  • The subtype should not weaken postconditions of the base type.

Let's say our Pigeon is some kind of mutant and doesn't actually lay eggs. We'll call it a MutantPigeon. Instead, no egg comes out at all:
class MutantPigeon < Bird
  def lay_egg
    nil
  end
end

bird = MutantPigeon.new

egg = bird.lay_egg # returns nil
egg.hatch! # raises an error: undefined method 'hatch!' for nil:NilClass
We've broken our program yet again! Since we've actually made the postconditions in our method less restrictive than in the Bird class, we've violated the Liskov Substitution Principle.

Instead, let's say that MutantPigeons actually return a more specific type of Egg. We'll call it MutantPigeonEgg, and it behaves just like Egg with a hatch! method. Then, we've strengthened the postconditions and are well within our rule:
class MutantPigeon < Bird
  def lay_egg
    MutantPigeonEgg.new
  end
end

egg = bird.lay_egg # returns nil
egg.hatch! # Returns a new MutantPigeon
And our program is happy again!

  • The subtype should preserve invariants of the base type.

Let's model a different bird this time. What about Penguins? As many people know, most penguins in the real world don't actually fly. So, we'll override the fly method with a no-op:
class Penguin < Bird
  def fly
    # no-op, do nothing
  end
end

bird = Penguin.new

bird.fly! # @flying != true
Looks like another break in our program! By doing nothing in our new fly method, we've broken the guarantee that the state of our @flying variable would be "true". Again, we've violated the Liskov Substitution Principle.

Now, this introduces an interesting problem. Penguins cannot just be made to fly, right?!

Real-Life Relationships != Inheritance-Model Relationships

Objects in the real world may show an obvious inheritance relationship. However, in object-oriented design, we only care about inheritance relationships regarding object behavior. Think of the classes in our system as representations of real-world objects. Those representations are fully defined by their external behavior (or interface).

Sure, penguins are birds in the real world, but Penguins are not Birds in our system because they do not behave like Birds. They don't have a properly functioning fly method.

Liskov Substitution and the Open/Closed Principle

Consider the examples above. Suppose we actually violated the Liskov Substitution Principle by creating our Pigeon class with a more restrictive eat method? Our existing program would have to be modified to handle our new class:
class Pigeon < Bird
  def eat(food)
    if ['bread'].include?(food)
      "Ate #{food}!"
    else
      raise "Does not eat #{food}!"
    end
  end
end

if bird.instance_of?(Pigeon)
  bird.eat('bread') # "Ate bread!"
else
  bird.eat('worm') # "Ate worm!"
end
As we know from the Open/Closed Principle, we shouldn't have to change existing code to add new requirements or features. By violating the Liskov Substitution Principle, we are forced to violate the Open/Closed Principle!

Conclusion

As with all programming principles, it's important to find a balance when applying the Liskov Substitution Principle in real-world scenarios. There is some debate over the benefits or detriments of the principle. Always keep it simple first, then refactor as needed.

Happy coding!

Thursday, February 12, 2015

SOLID Review: Open/Closed Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

The Open/Closed Principle was first coined by Bertrand Meyer in his book Object Oriented Software Construction. Meyer states that the implementation of any class in a system should be changed only to correct errors. Any new features are introduced by creating additional classes that extend or modify the existing code.

Meyer's idea is more popularly described as follows:

"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."

Following this principle brings a major benefit. We are less likely to break the existing system's functionality if we minimize any changes to the original implementation. This increases stability, extensibility, and maintainability.

Open for Extension, Closed for Modification

Classes abiding by the Open/Closed Principle exhibit two important characteristics: they are open for extension and closed for modification.

A class is closed for modification when its internal implementation is hidden away. Its only interactions with the outside world are through a set of public methods known as its interface. All its internal logic is assumed to be correct. Therefore, it shouldn't need to change.

A class is open for extension if its behavior can be enhanced or modified by adding new code on top of the existing implementation. Classes must be designed in a way that lets consumers "plug in" or "inject" new logic.

Abstracting Behaviors

The main key in adhering to the Open/Closed Principle is proper abstraction of key behaviors. These behaviors are abstracted and encapsulated nicely behind a shared interface. By keeping classes dependent on these abstractions, new behaviors can easily be introduced without changing the existing code.

A Simple String Transformer

Suppose we need a simple application that transforms strings. Somewhere in our code, we have a service object called Transformer which takes a string and transforms it into a some other object:
require 'json'

class Transformer
  def initialize(string)
    @string = string
  end

  def transformed_string
    JSON.parse(@string)
  end
end

Transformer.new('{"foo": "bar"}').transformed_string
# { "foo" => "bar" }

Simple enough! We can transform strings into Ruby hashes. Now, a new feature requires us to transform strings into binary in addition to Ruby hashes. Let's add the new functionality to our Transformer class:
require 'json'

class Transformer
  def initialize(string, type)
    @string = string
    @type = type
  end

  def transformed_string
    if @type == :json
      JSON.parse(@string)
    elsif @type == :binary
      @string.unpack('B*').first
    end
  end
end

Transformer.new('Hello', :binary).transformed_string
# "0100100001100101011011000110110001101111"
Great! Now we can pass in strings and specify the type of transformation to use. So far, so good. However, yet another new feature requires us to add support for yet another transformation: converting to MD5.

require 'json'
require 'digest'

class Transformer
  def initialize(string, type)
    @string = string
    @type = type
  end

  def transformed_string
    if @type == :json
      JSON.parse(@string)
    elsif @type == :binary
      @string.unpack('B*').first
    elsif @type == :md5
      Digest::MD5.hexdigest @string
    end
  end
end

Transformer.new('Hello', :md5).transformed_string
# "8b1a9953c4611296a827abf8c47804d7"
As you can see, our transformed_string method is starting to get quite ugly. It is also brittle, as we keep modifying the logic inside to accommodate new features! How can we make this class more open to extension?

Find and Extract the Abstraction

To make Transformer more open to extension, we need to make it depend on an abstract behavior rather than handling many different transformations. It seems like we keep on adding new types of transformations to our class, so let's abstract this behavior out!

Solution: Inheritance

We'll start by turning our Transformer into an abstract base class.
class Transformer
  def initialize(string, type)
    @string = string
    @type = type
  end

  def transformed_string
    raise 'Implement me!'
  end
end
The class looks the same. However, the application will now depend on Transform's sub-classes to implement the transformed_string behavior. Taking this approach, we can now create new types of transformations by adding new classes:
class MD5Transformer < Transformer
  def transformed_string
    Digest::MD5.hexdigest @string
  end
end

MD5Transformer.new('Hello').transformed_string
# "8b1a9953c4611296a827abf8c47804d7"
However, we've almost completely rewrote our existing implementation to make way for this solution. What about other classes in our application that depended on instances of Transformer? We would have to change class-names and signatures all over our application to accommodate our refactor.

Better Solution: Dependency Injection

Again, we want to extract and encapsulate the transformation behavior out and make Transform depend on an abstraction. We can achieve this by creating different Transformations and injecting them into Transform through its constructor:
class Transformer
  def initialize(string, transformation)
    @string = string
    @transformation = transformation
  end

  def transformed_string
    @transformation.transform(string)
  end
end

class BinaryTransformation
  def self.transform(string)
    string.unpack('B*').first
  end
end

Transformer.new('Hello', BinaryTransformation).transformed_string
# "0100100001100101011011000110110001101111"
This is a bit better, as the signature of our constructor hardly changes, but the implementation of Transform now depends on an abstraction known as Transformation. Any new (or existing) transformation behaviors can be added by creating new classes and injecting them into Transform!
require 'json'
require 'digest'

class JSONTransformation
  def self.transform(string)
    JSON.parse(string)
  end
end

Transformer.new('{"foo": "bar"}', JSONTransformation).transformed_string
# { "foo" => "bar" }

class MD5Transformation
  def self.transform(string)
    Digest::MD5.hexdigest string
  end
end

Transformer.new('Hello', MD5Transformation).transformed_string
# "8b1a9953c4611296a827abf8c47804d7"

Conclusion

This is a very simple example of how to design classes that are open for extension and closed for modification. It's important to remember to balance this principle against real-life requirements. If applied too soon, the Open/Closed Principle might lead to unnecessary abstractions, making code difficult to understand. Always take the simplest approach first. Then, if necessary, refactor code with the Open/Closed principle in mind.

Happy coding!

Friday, February 6, 2015

SOLID Review: Single Responsibility Principle


Note: This is part of a series of articles reviewing the five SOLID Principles of object-oriented programming.

The Single Responsibility Principle was first coined by Robert Martin in an article on the Principles of Object Oriented Design. To this day, it remains an important design principle because it encourages a lowly-coupled system with highly-cohesive classes. This type of system is much more maintainable because it is easier to modify.

Before we dive into the principle itself, we need to address two important concepts in object-oriented programming: coupling and cohesion.

Coupling

Coupling is used to describe the degree of dependency between individual classes. Classes are "highly coupled" if one class is directly affected by the behavior of another class in the system. If many classes are dependent on each other, a change in one may lead to a breaking ripple effect! This makes the system hard to change because many more parts need to be tested, fixed, and deployed.

We should always strive for classes that aren't too dependent on each other. This leads to a "lowly coupled" system, which is much easier to maintain.

Cohesion

Cohesion is used to describe the degree of similarity between the internal elements of a single class. A class with a variety of methods spanning many unrelated behaviors is said to have "low cohesion". If a class is designed this way, it will have dependencies with several unrelated classes. This leads to a monolithic class with tangles over many different parts of the system!

We should always strive for classes that encapsulate very closely-related behaviors. This leads to a set of "highly cohesive" classes, making it easier to decouple different parts of the system.

Cohesion and Decoupling through Single Responsibility

Robert Martin describes the Single Responsibility Principle as:

"A class should have only one reason to change."

In other words, a class should be responsible for providing only one specific function or behavior in a given system. When a class only has one responsibility, its internal elements are sure to be closely related, making it very highly cohesive. A system with many small, focused classes will have less dependencies because no single class will have to interact with too many other classes. This helps keep the system lowly coupled.

Let's look at a simple example.

Breaking Down a Monolithic Class

Suppose we have a simple class for representing bodies of text. We'll call it Document:

class Document
  attr_accessor :author, :text

  def initialize(author, text)
    @author = author
    @text = text
  end

  def save_to_file(filename)
    File.open(filename, 'w') do |file|
      file.write(full_text)
    end
  end

  def print_as_pdf
    pdf_creator = PDFCreator.new(full_text)
    pdf_creator.print
  end

  def print_as_html
    html_creator = HTMLCreator.new(full_text)
    html_creator.print
  end

  def send_to_email(email)
    email_sender = Mailer.new(email)
    email_sender.send(full_text)
  end

  def full_text
    "Author: #{@author}, Text: #{@text}"
  end
end

Here is a list of all the behaviors Document is responsible for:
  • Saving the text to disk.
  • Printing the text as a PDF or HTML page.
  • Sending the text via email.
This class lacks cohesion because it has various groups of methods each serving different purposes. Since many other classes may need to save, print, and send Documents, they will have to couple to this single, monolithic class.

How about we break up each individual responsibility into its own class?

Small, Single-Responsibility Classes

Using our list of behaviors, we'll create new classes for each responsibility.

Saving the text to disk.

We'll create a class whose sole purpose is to save text to the file system. We'll call it DocumentFile:

class DocumentFile
  def initialize(filename, document)
    @filename = filename
    @document = document
  end

  def save!
    File.open(filename, 'w') do |file|
      file.write(document.full_text)
    end
  end
end

Now, our Document can make use of DocumentFile to write its text contents to disk:

document = Document.new('Thomas Harris', 'The story of Silence of the Lambs...')
file = DocumentFile.new('silence.txt', document)

data_file.save!

If we wanted to add the ability to load an existing Document from the file system, we have a dedicated class for doing file input/output. We don't have to clutter our Document class to add functionality.

Printing the text as a PDF or HTML page.

We'll extract any methods for printing different formats out into a class called DocumentPrinter. This will make use of two imaginary Gems named PDFCreator and HTMLCreator. Their implementations aren't important, but it helps describe the behavior of the methods:

class DocumentPrinter
  def initialize(document)
    @document = document
  end

  def print_as_pdf
    pdf_creator = PDFCreator.new(@document.full_text)
    pdf_creator.print
  end

  def print_as_html
    html_creator = HTMLCreator.new(@document.full_text)
    html_creator.print
  end
end

Simple enough. Now, we have a class dedicated to printing different formats of our Documents:

document = Document.new('Stephen King', 'The story of Birds...')
document_printer = DocumentPrinter.new(document)

document_printer.print_as_pdf # Results in PDF data being printed to screen.
document_printer.print_as_html # Results in HTML being printed to screen.

Exercise: This could be taken even further by breaking each method and creating two new classes: DocumentPDFPrinter and DocumentHTMLPrinter. Each could implement a Printer interface. This might be a good idea, since we may require printing of other formats in the future.

Sending the text via email.

Finally, we'll create a new class whose sole responsibility is to send the Document to someone via email. Again, this will make use of an imaginary Gem named Mailer. We'll call our new class DocumentSender:
class DocumentSender
  def initialize(document)
    @document = document
  end

  def send_to_email(email)
    email_sender = Mailer.new(email)
    email_sender.send(@document.full_text)
  end
end
Now, we have a class whose only responsibility is sending a Document over email.

document = Document.new('Stephen King', 'The story of Birds...')
document_sender = DocumentSender.new(document)

document_sender.send_to_email('test@test.com') # Sends text in document to test@test.com.

Conclusion

In the end, we have three individual classes, each with their own responsibilities. If we need to add another feature, we can create another class without affecting any of the existing classes. We can more easily modify existing functionality because we can pinpoint where changes need to be made based on each responsibility.

This whole example can be taken even further by making our Sender and File more generic. In other words, decouple them from Document by taking any data and sending or saving it. I'll leave that as an exercise for the reader.

Happy coding!

Wednesday, January 16, 2013

Personal Lessons from Jeff Atwood's "Effective Programming"



I recently finished Jeff Atwood's blog-turned-book: Effective Programming: More Than Writing Code. It's a compilation of blog entries about different ways to improve programming ability outside of the typical "study and follow a code tutorial" method. The whole book is worth a read, but here are some interesting points I took away from the book and the names of their chapters:

1. For an easy way to improve programming ability, read programming blogs.

"When we read a post, or a book, or look at a new language, let's assume that some or even most of it will not be new. Let's assume that we'll positively detest some of it. But let's also look at it in terms of our own profit: we win if we can find just one thing in there that makes us better programmers." - Sharpening the Saw

Reading code and picking up syntactical candy is an effective way to improve coding ability, but the process can get tedious and boring. The same goes for software textbooks, computer science papers, and online tutorials. Some programming blogs are just as effective as all of the above, and far less boring. They're much more personal and often stem from real life situations. An abundance of programming articles can be found at aggregate sites like HackerNews. There are thousands of submissions, but every once in awhile I'll stumble upon a blog entry that I can personally "profit" from. The gains vary, but I think programming blogs provide a fine balance between education and entertainment.

2. The programmer can lie, the documentation can lie, but the code never lies. You must be able to read other people's code.

"No matter what the documentation says, the source code is the ultimate truth, the best and most definitive and up-to-date documentation you're likely to find." - Learn to Read the Source, Luke

Programmers are lazy. Sometimes it works in our favor, other times it can work against us. Refusing to dig into source is where it can work against us. I'm sure there are exceptions, but we should be able to open up and step through most of the code we work with. Wikis and docs are great guidelines for understand pieces of software, but the only absolute truth of a program lies in its source! I believe most programmers spend more time reading code than writing it. Anyone can hack together software. It takes special talent to quickly understand how a program works by exploring the code.

3. A software developer's ability to learn is hundreds of times more valuable than his or her experiences.

"It's been shown time and time again that there is no correlation between years of experience and skill in programming. After about six to twelve months working in any particular technology stack, you either get it or you don't." - The Years of Experience Myth

Experience is very valuable, but it should be a direct result of learning. A year of experience means nothing if it was spent mindlessly fixing minor bugs or recycling bad programming habits. After every job or personal project, you should have a set of new tools added to your toolbox. Software developers who are constantly learning also provide a steady stream of valuable information to the teams they work with. Not only do they protect themselves from obsolescence, but their coworkers as well.

4. Don't be shy, allow other people to read your code.

"When your code is reviewed by another human being - whether that person is sitting right next to you, or thousands of miles away - you will produce better software." - Pair Programming versus Code Review

Whether it's a young developer or a seasoned veteran, there are times a programmer may feel compelled to keep his or her code from others. A senior developer might feel a stubborn sense of ownership over code, not trusting it to any coworkers. A rookie might be too shy, afraid to look stupid in front of the team. Either way, this can lead to a huge un-maintainable blob of spaghetti code. As a young developer myself, I've concluded that feeling a little stupid in the moment someone criticizes your code is better than feeling utterly humiliated when the entire team sees the ugly mess you've swept under the rug over time. If you're lucky enough to work with a rockstar group of developers, take advantage of the opportunity and let them review your work!

5. Great programmers are good for your team. Great programmers who are "bad apples" are absolutely and utterly terrible.

"You should never be afraid to remove - or even fire - people who do not have the best interests of the team at heart. You can develop skill, but you can't develop a positive attitude." - Dealing With Bad Apples

We all know what "bad apples" are. They have all the qualifications of a great developer, but just don't fit into the "culture" of the team. While their skills provide value, their effect on everyone else is destructive. I've had the great pleasure of working with different types of bad apples. Some were able to hang around, thus slowly killing the team around them. Some left, allowing everyone to breathe a sigh of relief. Without bad apples, communication greatly improves. The team can get work done very effectively and, more importantly, together. Hiring good programmers who "fit in" is better than hiring great programmers who suck as human beings.

6. Spend more money on life experiences than on things.

"Things get old. Things become ordinary. Things stay the same. Things wear out. Things are difficult to share. But experiences are totally unique; they shine like diamonds in your memory, often more brightly every year, and the can be shared forever." - Buying Happiness

This one is a no-brainer, but it's always nice to see a little reminder of it. It's a classic lesson in finding happiness. I like to view the "experience vs thing" argument in terms of opportunity cost. I could go out and buy a brand new BMW to replace my aging Nissan. My preferred down payment for one of these bad boys ranges from $5000 to $8000. A brand new car would bring some glamour and a bit of fame among friends, but it would last about a week. Then, it becomes just another car on the road (an expensive one at that). A BMW would be awesome, but with that money, I could fly out to Argentina and hike around Patagonia. I wouldn't have the luxury of rolling around in the "ultimate driving machine", but I'd have memories of being in one of the most beautiful places on earth. Again, this is a no-brainer.