I think my favorite programming book of all time is The Pragmatic Programmer. It was the first introduction I had to being more intentional with my programming. There were certain types of programming patterns that felt good, but I didn't know why. This book taught me why those things were good, what name people had given to those good things, and showed me the next steps to take in those areas of good.
James Marcus Bach had a series of tweets on Tuesday that reminded me of the Pragmatic Programmer's section, "The Requirements Pit." I still haven't fully grasped the depth of the wisdom of this quote from the book:
Requirements rarely lie on the surface. Normally, they're buried deep beneath layers of assumptions, misconceptions, and politics.
Here's what James had to say on twitter:
Here's the typical pattern for requirements definition on software products:
"What do you want?"
"We want this!"
"You sure?"
"Yes!"
"This is what that looks like."
"Oh, we didn't what THAT."
"Okay, what do you want?"
The purpose of testing is largely to force everyone to see the detailed implications of their vague daydreams.
This resonates with my experiences very well. There's especially a problem if you don't take the time up front to say, "This is what that looks like." Because the next opportunity for the end user to find out that what they asked for isn't what they wanted is after a bunch of time and money has been spent.
Near the end of .NET Rocks show #582 Richard Campbell explains hyperthreading in such a way that I feel like a hardware expert.
The guest, Scott Hanselman, has a 6 core hyperthreaded processor, which shows up at 12 cores in Windows Task Manager. How many cores does this CPU actually have?
Richard: You have six, with six brain-damaged cousins. And sometimes you can hand the cousin, "Here, eat this, while I'm doing the real work." And everything is fine. But once in a while he chokes. And then the brother actually has to pat him on the back to get him cleaned up.
Carl: He's gotta do the Heimlich on his cousin.
Richard: That's exactly what happens. When the pipeline drains, it is the Heimlich. You are barfing 64-bits of instructions out of the core.
Now I know, and will never forget (no matter how hard I try), how hyperthreading works.
A coworker sent me this today; this idea had never crossed my mind:
Not sure if you’ve already got something setup to automatically update your svn checkouts, but thought I’d mention this. I just created a .bat file:
TortoiseProc.exe /command:update /path:"C:\Code\Path\to\Proj1" /closeonend:1
TortoiseProc.exe /command:update /path:"C:\Code\Path\to\Proj2" /closeonend:1
etc...
Then setup a windows task to run the file everyday at 4:30am. No more out of date code!
Well, that's cool. Two benefits I see to this:
- If you happen to go for several days without checking in (not a good practice, but it happens) your chance of getting a nasty merge conflict is reduced.
- If you switch over to a project that you haven't worked on for a while you don't have to wait for a long, drawn-out update to execute before you can jump right in.
After listening to .NET Rocks Episode 562, "Teaching Kids Programming," I downloaded Small Basic again to play around a bit. I uploaded a little triangle fractal program that's available here:
http://smallbasic.com/program/?ZTR830
Here are a couple notables:
- The web-based view scales down the graphics, doesn't appear to support PenWidth, and doesn't draw as smoothly as the installed IDE does, so it's a little less nifty through the above URL.
- As far as I can tell, subroutines in Small Basic don't support parameters, which makes recursion a little more difficult, which makes fractals a little more interesting since they're naturally recursive. It was fun to work around this with a Stack.
- I'm so used to reducing the scope of variables as far as possible in my daily work, and avoiding the use of global variables. Small Basic feels funny because everything is global. (I definitely think that makes good sense considering what Small Basic is intended for.)
- It was a good trigonometry review. I had an inkling that ArcTan was going to give me some trouble, but I couldn't remember exactly what. After a bit of trial and error I have it down that if we're on the left side of the unit circle we need to go in the opposite direction. Adjusting the angle by 180 degrees doesn't seem to cut it.
- I played around with some recipes at http://teachingkidsprogramming.org/, and can see how that technique might help kids learn things pretty well. Maybe I'll try that with my oldest two this summer.
If you use log4net for logging in the traditional way you end up having a lot of classes with the following line:
Private Shared mLog As log4net.ILog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
This is the VB version of the recommendation from the log4net FAQ (see the section, "How do I get the fully-qualified name of a class in a static block?").
In order to tell CodeRush, "whenever I type l4n and then a space, fill in the above text," I created a new CodeRush template for l4n with the above text. I used the following condition:
(InType and OnEmptyLine) but not (InComment or InMethod or InProperty or InString)
CodeMinder: to remind myself of stuff that I usually have to look up each time I do it.
Notice the unnamed default in the following line:
alter table test add col1 int not null default 1
This creates a default constraint in the database that is named something like “DF__test__col1__182C9B23,” which is pretty ugly. The last several characters are some sort of random-ish sequence, and will be different on everybody’s database. The difficulty comes later if some needs to modify this table:
alter table test drop column col1
You’ll get the error:
The object 'DF__test__col1__182C9B23' is dependent on column 'col1'.
ALTER TABLE DROP COLUMN col1 failed because one or more objects access this column.
So to drop the column you have to first drop the constraint:
alter table test drop constraint DF__test__col1__182C9B23
Checking this script into source control is probably a big mistake. That script will only work on my database – everyone else will get this error, because their constraint is named something else. They’ll get the following error:
'DF__test__col1__182C9B23' is not a constraint.
Could not drop constraint. See previous errors.
So rewind to the beginning. Instead of this:
alter table test add col1 int not null default 1
Do this:
alter table test add col1 int not null constraint DF_test_col1 default 1
Now everyone’s default constraint is named the same, and it’s trivial to write a script that will drop the constraint on anyone’s database. I think it’s a fairly standard naming convention for default constraints to be DF_[table name]_[column name].
By the way, here is the definition of a stored procedure that can be used to drop unnamed default constraints.
create proc DropColumnDefault (
@table_name nvarchar(256),
@col_name nvarchar(256)
) as
declare @Command nvarchar(1000)
select @Command = 'ALTER TABLE ' + @table_name + ' drop constraint ' + d.name
from sys.tables t
join sys.default_constraints d on d.parent_object_id = t.object_id
join sys.columns c on c.object_id = t.object_id and c.column_id = d.parent_column_id
where t.name = @table_name and c.name = @col_name
if @Command is not null
execute (@Command)
CodeMinder: to remind myself of stuff that I usually have to look up each time I do it.
To delete all PersonAlternativeName nodes from XmlColumn of the People table:
with xmlnamespaces(
'http://mydomain.com/person/extension/0.1' as px)
update People
set XmlColumn.modify('delete //px:PersonAlternateName')
CodeMinder: to remind myself of stuff that I usually have to look up each time I do it.
If a People table has a column named XML, and the XML column may specify multiple PersonAlternativeName entities, then to get all PersonAlternativeNames in a single result set use CROSS APPLY or OUTER APPLY.
with xmlnamespaces(
'http://mydomain.com/person/extension/0.1' as px,
'http://niem.gov/niem/niem-core/2.0' as n)
select p.PersonId,
n.value('(n:PersonGivenName)[1]', 'varchar(100)') as FN,
n.value('(n:PersonMiddleName)[1]', 'varchar(100)') as MN,
n.value('(n:PersonSurName)[1]', 'varchar(100)') as LN
from People p
cross apply XmlColumn.nodes('//px:PersonAlternateName') as ppl(n)
"Learn the principle, abide by the principle, and dissolve the principle."
- Bruce Lee
I first saw this line in one of Ron Jeffries's sig lines years ago. It struck me then and has stuck with me over the years.
I was reminded of it a few months ago when I read this observation about Scrum:
The intention of Scrum is to make [an organization's inadequacy or dysfunction] transparent so the organization can fix them. Unfortunately, many organizations change Scrum to accommodate the inadequacies or dysfunctions instead of solving them.
The latest experience that reminded me of this was Udi Dahan's week long eye-opening Advanced Distributed System Design class. It's been like Yoda's teaching Luke Skywalker, "You must unlearn what you have learned."
Bruce Lee's thought has particular applications for software developers. I'll explain my interpretation of what he was saying and then apply it to software development.
Learn the principle
Step one is pretty simple: learn the subject you are exploring. If you want to do x, where x could be agile, scrum, TDD, DDD, CQRS, etc., there are tons of material available. When you feel like you've found a trustworthy source for the x that you currently want to do it'll provide instructions: break work into user stories, write a test for something before you build it, make sure a class has only one reason to change, etc. You’ll need to discover for your x what are the things to do.
Warnings:
- If you learn to do a Judo Ashi Guruma throw, that’s great and may even be useful in some situations, but if you start telling people that you "do Judo" just because of that, the people who really do do Judo are going to be annoyed with you and Ashi Guruma you to the floor. Keep this in mind when you casually leaf through the Blue Book and learn about entities, value objects, and repositories. Those chapters are an exciting read and the tools are useful. But be careful claiming to be "doing DDD." The first letter of DDD is for domain, not design; there may be a few more important things about DDD that you haven't picked up yet, and the people who have picked those up may get annoyed if you claim you're doing DDD just because you have an entity and an IRepository<T>.
- No matter how much you try to jump into Tae Kwon Do by learning the Reverse Spinning Hook Kick, you may not get it. That’s because it's hard, and it builds on a foundation of other things that must come first. "But I don't have time for that; I just want a nifty move so I can start trashing bozos." Our tendency is to blame Tae Kwon Do. "I tried it; it didn’t work for me." So goes the parable of the organizations who tried XP but it didn't work for them. Did you work in iterations? Was your customer available? Did you try to fix it when it broke? The answer is usually "No, there wasn't time for all that; we have deadlines after all." But all of XP working together makes a cohesive solution, and each part builds and supports the whole. Be careful about picking and choosing what you'll try. If everyone around you is raving about peaches and cream, and you try it but leave out the cream, you'll be missing out, and may give up on the peaches without ever knowing what you're missing.
Abide by the principle
Now that you've learned a few things to do, you need to do the things you've learned. Yes, you really do need to write a unit test for that piece of business logic you're about to hack into that class. I understand that it's a small change and this class isn't quite set up for unit testing and it may take you 25 minutes instead of 5 minutes to make the change. Daniel-san, abide by the principle: wax on, wax off.
The Karate Kid thought he was just doing a bunch of work for Mr. Miyagi, painting the fence, painting the house, sanding the floor, and waxing the cars. It was really frustrating for him: why do I have to do all this stupid work? I just want to do karate! But in order to learn to do karate, he had to do all the "stupid stuff" first. In fact, the stupid stuff was the path to doing karate. Daniel was very surprised after all the hard, tedious work was done to find he could block punches and kicks like an expert because of all the "stupid stuff."
Pursuit of mastery requires learning through experience, not just academic study. There are things that are too subtle to see without sharpening our perception through practice. So when the frustration mounts and you feel like quitting or compromising, take heart that you may be at the threshold of a meaningful step in your journey.
Warnings:
Now in The Karate Kid, Part III, Daniel gets a new sensei, Terry Silver, who is actually Daniel's enemy. Silver teaches Daniel bad techniques that are painful in the short-term and harmful for his long-term technique.
Silver meant harm; I know of no software development teacher who means harm. Nonetheless there are materials out there that mean well, but if you follow their teaching you'll experience pain in the short-term and have to deprogram yourself later in order to progress. It pains me to see the casualties of bad teaching engrained by years of use, and the technological fallout. Beware the false teachers; in your pursuit of good material consider what Jesus Christ says about false prophets: "By your fruits you will know them." His words have more wisdom than appear on the surface: fruit is not evident right away; it takes time to be manifested, and until it fully manifests itself it takes a trained eye to identify whether the fruit will be good or bad.
Dissolve the principle
Once you've learned some good techniques, obeyed them long enough to learn some of their more finer points, and given them time to bear some fruit to see whether you like the way it tastes, another important consideration remains: dissolve the principle.
Bruce Lee expounds with the following:
...In short, enter a mold without being caged in it. Obey the principle without being bound by it.
Lee invented Jeet Kune Do, a system of fighting that "works on the use of different 'tools' for different situations." Opponents using a single martial art may become predictable, forced to operate within his art's implicit limits. The principles of any one way give strength and have their utility, but come with limits, and for you to exceed those limits you must know the principle well enough to know when it is out of place, when to dissolve it.
Domain models are great and all the rage. Everyone should probably always use one, right? Martin Fowler in his great book gives four paragraphs describing when to use it (p119), one of the main criteria being "if you have complicated and everchanging business rules." How many of us have Customer.FirstName in our domain model? Is that really a complicated and everchanging business rule?
Everyone should always use a layered architecture, right? Does that necessarily mean we draw huge horizontal lines through every application? Are there other ways to obtain separation of concerns? Are there parts of my application where I'm actually limited by NHibernate?
As the old adage goes, and is oft repeated by software developers, if all you have is a hammer, everything looks like a nail. You can pound a screw into a board with a hammer, but it sure takes a lot of effort.
Warnings:
So does this last section sooth your conscience from those design decisions you didn’t feel good about, where you didn’t abide by the principle? Do not too quickly "tip the vessel of knowledge," and earn yourself an architectural boot to the head. The tendency of most of us is to quit the guiding principles too soon through impatience. Don't abandon the principle and think you're dissolving the principle. When you dissolve something in water, it's still there, it just no longer exists as a distinct thing; it becomes an integral part of something more than itself.