How religion is important

I begin to wonder whether I am mad or have hit on an idea which is much bigger than I am.

Gregory Bateson

As someone who grew up in a religion and then let go of it in my mid-twenties, it’s easy to say, religion is a useless fiction that persists because a powerful group finds it useful.

Bateson (an atheist in a family of atheists) has a bigger idea. He believes that religions exist to hold the “everything else” of whether and why we should do a thing. To hold all the systemic and invisible-to-consciousness reasons for an action. They are the foil to strait-line purpose.

“Supernatural entities of religion are, in some sort, cybernetic models built into the larger cybernetic system [our culture] in order to correct for noncybernetic computation in a part of that system [our conscious, purposive minds].” (this from a letter; thanks to @gdinwiddie for leading me to it.)

As people in our (capitalist) culture, we aim to meet goals. Those goals accomplish something, and have some side effects that are very hard to notice or measure. Bateson proposes that religion is designed to account for all of the rest of those effects.

Can we come up with a way to notice the effects of our actions, wider than the progress toward our goals, that is not based on the fiction of existing religions?

Evaluating the Value of Code

In retail marketing, there is a metric of customer importance called RFM: Recency, Frequency, Monetary value. How long ago was the last order? How many times have they ordered? How much money have they spent with us? This determines how valuable the customer is, and how much effort the company should put in to keeping them happy.

R + F + M => $$$

At ScanDev, Dan North spoke about patterns of development that he observed in a phenomenally productive, short-turnaround, high-quality team. This team changed the application daily, reacting to new requirements instantly. Yet, the codebase didn’t degrade over time. Why? How were they able to accomplish what we keep telling our managers is impossible?

For one thing, they recognized: not all code is equally valuable.

Dan’s team didn’t refactor each feature to near-perfection before committing it and trying out in production. Many features would change or get deleted within a few weeks of implementation. Time spent refactoring features that soon become obsolete is worse than wasted: reducing duplication means introducing indirection, which makes other code less simple.

Don’t get me wrong – Dan’s team refactored constantly. But they spent their refactoring energy on code that had proved its value, rather than on today’s hot new thing. They put module-level tests around the new stuff, and deeper and more detailed tests around code that turned out to be critical. They let time pass before determining which code was important. Much like developers suck at predicting performance problems as we code[1], and we’re lousy at predicting change[2], sometimes we also stink at predicting which piece of code is important.

What if we could measure the value of code the way retailers evaluate customers?
There are metrics we could use to suggest an expected value of code – the importance of the code to the business and to developers.

R – Recency: the more recently the code was written, the lower its expected value. Our VCS can tell us.
F – Frequency: how often is the code executed in production? We could sample this with a profiler.
M – Modification: how many times has the code changed? VCS has the answer.

What if these three metrics were compared against estimates of localized technical debt? Tools like SONAR quantify technical debt, but they say nothing about code value. If we could stack up code coverage, style violations, and complexity against the importance of a piece of code, this would help us focus our refactoring efforts.

What of the low-value, high-technical-debt code? The ugly stuff that’s still accumulating in our codebase because it is not important enough to fix? Delete it. A premise of the delayed-refactoring   strategy is that some code will go away. Pruning little-used features out of the codebase is necessary to keep it sane. Deleting code is the best refactoring: it’s like declaring technical-debt bankruptcy, but without the bad credit rating.[3]

Google does this, nixing the least-used features and products. We think, “Why did they take it away? It was already working, it wasn’t costing them anything!” but it was. Because every new feature comes with that silent requirement “… and everything else works too.” And the rat’s nest grows.[4]

Another potential problem: what if we go back to refactor and we can’t figure out what the code does? Ay, there’s the rub. Delaying refactoring doesn’t mean writing cryptic code. It is even more essential that your code convey its intent with good naming and concrete expressions of what you’re doing. Avoid abstraction. Let repetitive code live a while longer. Keep it contained, though: favor adding code over changing existing code in many places, even if that means you’re copying and pasting.

The Last Responsible Moment for design is later than you think, and not all technical debt is created equal. There is a cost to careful design, and we can weigh this cost against a value only measured in production.

————
[1] Premature Optimization Is The Root Of All Evil — Knuth
[2] You Ain’t Gonna Need It — XP
[3] It’s like promising your kid chocolate, and then by snacktime she decides she doesn’t like chocolate anymore and you get to eat the chocolate. Good-bye, bad code. Hello, faster compile times!
[4] There’s this great song by Bobaloo that goes,

“My hair had a party last night.
When I laid down everything was all right.
It started out friendly but there must have been a fight!
My hair had a party last night.” 

Feature-heavy applications feel like that. Gotta brush ’em out and split ’em up. Or cut ’em off.

Strong Typing in Java: a religious argument

Strongly-typed, type-inferred languages like F#, Scala, and Haskell make Java feel like its static typing system is half-assed. This is not Java’s fault entirely; it’s the way we use it.

Say we have a domain class like


public class User {

public User(String firstName, String lastName, String email, long balanceInDollars) {}

...
}

I’m in agreement with Stephan that using String and primitive types here is wimping out. That is weak typing. Weak! Chuck Norris does not approve!

firstName and lastName and email are conceptually different. If each has their own type, then we get real compile-time type checking.


public class User {

public User(FirstName firstName, LastName lastName, EmailAddress email, Money balance) {}

...
}

This gives us type safety. As we pass a FirstName or an EmailAddress from class to class, we know what we’re getting. If we mix up the order of the arguments, we hear about it from the compiler.

“But they’re just strings!” you say. “Don’t make a bunch of cruft – just call them what they are!”

NO! I say. They are not Strings. They are stored as strings. That, my friend, is an implementation detail. FirstName and EmailAddress represent distinct concepts, and they should have distinct types. The first part of wisdom is calling things by their right names.

There are other OO-style benefits from this, such as putting the validation for each type in its type class and changing the internal representation of the type without affecting its interface. Those may be significant in some situations, but in my argument they’re icing. The benefit of strong typing is compile-time checking, and that’s reason enough to call a FirstName a FirstName and not a vague String.

Now, let’s address this “bunch of cruft” argument. No question, Java does not make this kind of type-wrapping pretty. In Haskell, it takes a one-line type alias. By OO principles, we ought to be able to inherit from String to get its behavior. But noooo, this is Java, and String is final, so we wind up with


public class FirstName {
public final String stringValue;

public FirstName(final String value) {
this.stringValue = value;
}

public String toString() {...}
public boolean equals() {...}
public int hashCode() {...}

}

(notice that I used a *gasp* public field. That’s another religious argument for another post. I include it here just to stir people up even further.)

Then every time we want a user:


User u = new User(new FirstName("Joe"), new LastName("Frank"), new EmailAddress("..."), new Money(30));

We can get a little better by providing static methods and statically importing them:


(in FirstName)

public static FirstName(final String value) {
return new FirstName(value);
}


User u = new User(FirstName("Joe"), LastName("Frank"), EmailAddress("..."), Money(30));

That’s a little better. But now we get into the part where user is an immutable value type, and we want to update one field.


User updated = new User("Joseph", old.getLastName(), old.getEmailAddress(), old.getBalance());

Ugh! and every time we add a new field to user, even though we simply want to copy it, we have to modify this code.
Copy constructors don’t help when the type is immutable.

Let’s talk about F# for a minute. F# has records:


type User = { firstName : FirstName; lastName : LastName ; email : EmailAddress; balance : Money }

and then the cool bit is, when you want another record that’s almost the same:


let updated = { oldUser with firstName = FirstName("Joseph") }

I want to do this with my Java user. I want to say


User updated = newUser.with(FirstName("Joseph"));

which is cool and all; with strong typing we can overload the “with” method all day long. We can chain them. We can add implementations of “with” for common combinations of fields to reduce instantiations.


(in User)

public User with(FirstName firstName) {
if (this.firstName.equals(firstName)) {
return this; // avoid instantiating an identical value object
}
return new User(firstName, this.lastName, this.email, this.balance);
}

Now you have a whole ton of “with” methods that can be chained. If you add a new field to User, you need to change all of them, but they’re all in the same place so that’s just fine. What changes together, stays together.

Now, if you don’t like an instantiation per changed field, or if you don’t like all those “with” methods cluttering up your user class, here’s another idea:


User updatedUser = new UserBuilder(oldUser).with(FirstName("Joseph").with(LastName("Frankenfurter").build();

where the UserBuilder keeps the oldUser and uses its values for any fields that aren’t provided by the caller to build the new user. That’s one instantiation, only one method that instantiates a user, and it’s encapsulated into one builder class.

Some people may argue that immutable types and strong typing in Java is going against the way the language is intended to be used, and therefore does nothing but make our lives more difficult. “That’s why God gave us POJOs,” they say. Java is a powerful language, and it is capable of supporting more idioms than the ones the language designers envisioned. We can grow as programmers within our language of choice. Java supports strong typing.

Strong typing gives us compiler errors on what would otherwise be caught only during testing. It creates some extra code, sure, but it’s localized. It can make working with immutable types a little cleaner.

I say, never use “String!”