Recontexting and Hygienic Code

When building software, we are often faced with a choice: Should we build just what we need now, or should we do extra work now to make things easier in the future? Consider this simple example:

  // 1. Simple
  GasGrill grill = new GasGrill();
  Hamburger h = new Hamburger();
  grill.cook(h, 375, 8);

This is great, but what if I want a charcoal grill? One alternative would be use a factory:

  // 2. Factory
  Grill grill = GrillFactory.newInstance();
  Hamburger h = new Hamburger();
  grill.cook(h, 375, 8);

Now, the details of grilling are hidden behind the GrillFactory, and we can choose another grill without modifying code. Or, I could avoid the dependency on factories with POJOs and dependency injection:

  // 3. Dependency Injection
  public class InjectionGrillAction {
    private Hamburger h;
    private Grill g;
    public Hamburger invoke() {
      g.cook(h, 375, 10);
      return h;
    }
  }

Agile practice argues "You Ain't Gonna Need It (YAGNI)." So we could build the simple, concrete implementation, and switch to factories or DI only when and if necessary.

Modifying code to be more flexible or general is one form of refactoring. Refactoring is a Good Idea, but in this case there is a Better Idea. What if we could recontext? Instead of changing the code, we change the context in which the code runs. Consider this example:

  # 4. Hygienic: grill_main.rb
  h = Hamburger.new
  g = Grill.new
  g.cook(h, 375, 8)

Is this an example of the Simple, Factory, or Dependency Injection approach? You can't tell, without context. Depending on what other code runs first, the Hygienic code might exemplify any of these approaches, or none. I can run the code in a Simple context:

  ruby -rsimple_context grill_main.rb
  => Hamburger is medium_rare and has a totally generic flavor

Or, I can run the code in an Injection context:

  ruby -rinjection_context grill_main.rb
  Hamburger is medium_rare and has a generic outdoor flavor

The injection framework I am using here supports injection through system properties, so we can inject a charcoal grill:

  export Grill=CharcoalGrill
  ruby -rinjection_context grill_main.rb
  => Hamburger is medium_rare and has a delicious charcoal flavor

The Hygienic approach is as simple as the Simple approach, and more powerful than the Dependency Injection approach. How is this possible? All of the other approaches commit to too much. Rather than simply solving the task at hand, the Simple, Factory, and DI approaches all embed presumptions about future needs. They either do not plan for the future at all (Simple), or they complicate the code with concerns peripheral to the task at hand (Factory, DI).

Hygienic code cleanly commits to solving the task at hand, and avoids committing to anything else. This is a simple idea, but look what it does to some sacred cows:

  • Factories (and most other design patterns) are code smells.
  • Refactoring IDEs may have done more harm than good. How often are you refactoring code that should be recontexted?
  • POJOs are a necessary evil. In more hygienic languages, the notion of a POJO is almost meaningless.
  • Domain-Specific Languages (DSLs) are a Good Thing. They naturally tend to be hygienic.

If you are interested in learning more about hygienic code, we are integrating this approach into our curriculum. Or, come to North Carolina and work with us.

Get In Touch