I learn a new language every year. This year, I had decided to learn a functional language. The contestants were many and varied, but I'm pleased to announce that the winner this year is...
I learn a new language every year. This year, I had decided to learn a functional language. The contestants were many and varied, but I'm pleased to announce that the winner this year is...
Posted at 09:29 PM | Permalink | Comments (1) | TrackBack (0)
As part of his talk on integration tests J.B. Rainsberger talked about how contract tests can be used to test the interaction between classes when using a mockist approach to developer testing. He wondered aloud if it would be possible to write these kinds of tests using abstract classes and JUnit 4. The answer is yes, with some caveats, as I demonstrate in the screencast below. Enjoy!
EDIT: If the embedded screencast below doesn't work for you, try this link.
Posted at 11:50 PM | Permalink | Comments (4) | TrackBack (0)
The quality of code is a reflection of the environment in which it was created. Chaotic environments usually create chaotic code. Professional environments usually create clean code. In my experience, it's one of the most reliable metrics that you can check before taking on a new client, employee or employer.
So ask to see code. Scan through it. Try to build and deploy it. Try to write a test. It only takes a few minutes and you can learn a lot.
Posted at 05:42 PM | Permalink | Comments (0) | TrackBack (0)
It wasn't because I was used to vim. I wasn't. In fact, aside from editing configuration files through ssh for the occasional sysadmin work I'm call upon to do, I'd hardly ever used vim before.
I started to investigate vim because I had hit a productivity plateau with TextMate. I had learned the majority of the useful shortcuts. I had installed all kinds of bundles, and even made a few. But I still had moments where I thought to myself "I'm wasting time with this", and I had no clear way to make them better.
I started to think that this was holding me back, not in the actual time it was taking, but in the way it affected how I write code. Programmers love efficiency. They tend to repeat actions that are efficient, and avoid those that aren't. As a result, your development environment has a direct effect on how you write code and what code you write. It encourages efficient actions and discourages inefficient ones. The problem is, what's efficient in your editor might not be the best thing for your codebase.
I wondered how I would write code differently if all my modifications had zero cost. If I could add methods, remove classes, rename variables and add new statements in the blink of an eye, I would make different choices about how I code...I don't think it would just be the same thing done faster. I might start writing more exploratory tests. I might use inline assertions more often. I might favor a failing test over firing up a debugger more often.
So I started worrying that inefficiencies in my work environment were encouraging me to take an approach that was, overall, less optimal. This has lead me to some pretty serious thought about what a truly optimal environment would be like. So far, I've come up with four properties of an ideal programming environment:
So the reason I switched from TextMate to vim, is that I think vim gets me closer to an ideal environment. Will it get me all the way there? I don't think so. I do think that I'm already more productive using vim (even after just a month of learning it) than I ever was with TextMate...and I'm still learning. I also think it's going to be a very long time before I hit a productivity wall like I did with TextMate. There's just so much to learn in vim.
Posted at 08:57 PM | Permalink | Comments (10) | TrackBack (0)
At the request of a former employer here's a tail recursive version of the prime factors kata in Ruby.
def generate(n, factors=[])
return factors if n == 1
new_factor = (2..n).find {|f| n % f == 0}
generate(n / new_factor, factors + [new_factor])
end
Posted at 02:46 PM | Permalink | Comments (0) | TrackBack (0)
Another option, rarely exercised, is to revert Tim's change to fix the build, and then email the team to let them know what happened. Really, it's nothing personal. Tim just make a simple mistake, he checked in when he wouldn't have enough time to check the build before going to his meeting. Now Bob (and the rest of the team) is stuck. If the fix isn't obvious, or will take a long time, the solution is simple: Just revert the commit.
Tim can recover his changes from source control when he gets back from his meeting and try again. If you can't (or don't) take responsibility for a broken build, and let everyone know that you're working on it, you should expect your commit could be reverted. Again, it's nothing personal...it's just the best thing for the team.
I think there's a sort of stigma associated with source control reverts. People think of it as destroying someone else's work. Sometimes this is hard, because often the thing that's breaking the build is a minor problem, and it's a mistake that we all make. But just as we've learned that refactoring isn't wasteful because it allows us to evolve our design, understand that reverting a commit isn't wasteful because it unblocks the team. And the changes don't go away...they're still in source control and easy for the original committer to recover. My suggestion is to keep this technique in your toolbox, and don't be afraid to use it. You should never be blocked due to a failing build.Posted at 08:54 PM | Permalink | Comments (0) | TrackBack (0)
My continuing thoughts on MVC...
We separate models, views and controllers, because it decreases the cost of change. It does not, however, allow us to replace one these layers without changing the others. That's rarely practical. If it happens, that's great, but don't count on it. The same thing is true in thin clients. It's a nice idea to think that you can redesign your site simply by replacing your CSS and leaving your markup and javascript alone. Unfortunately, that's usually not the case.
We decouple our systems along these lines because over time we've found that these particular configurations create effective firewalls to halt the ripple effects of change. A harbor doesn't prevent all waves from rocking the boats, it just makes the big ones smaller. MVC and it's ilk make maintenance easier by reducing the cost of change, not creating a plug-replaceable system. Plug-replaceable systems are expensive and difficult to create, and you should only make one if you need it for a specific business reason.
By extension, if your domain causes changes to frequently occur in other parts of the system, decoupling those parts from the rest will decrease the cost. And while I'm not going to tell you to forget MVC (or Model-View-Presenter, or any other paradigm), be sure that the separations you create actually insulate changes that tend to occur on your particular project, and aren't just there because you think they should be.
Posted at 08:16 PM | Permalink | Comments (1) | TrackBack (0)
Firefox's default keyboard shortcuts do a pretty darn good job of giving you everything you need to browse the web mouseless.
Backspace = Back Shift + Backspace = Forward Tab = Next Field (If you didn't know this one already...) / = Search Text ' = Search Links Only F3 = Search Again Once you've got a link selected: Enter = Open Link in current tab Ctrl/Cmd+Enter = Open Link in new tab Shift + Enter = Open link in new window Spacebar = Page Down Shift + Spacebar = Page Up CMD + L = Jump To Location Bar (CTRL + L on Windows, I think) Ctrl/Cmd+Tab = Switch tabs Ctrl/Cmd+[1-9] = Select tab (1-9)You can find the full list of firefox shortcuts here
Posted at 04:26 PM | Permalink | Comments (0) | TrackBack (0)
Posted at 04:53 PM | Permalink | Comments (3) | TrackBack (0)
It took me a fair bit of refactoring after I got the last test passing (generate 10 ) to get it this tight, but I like it.
def generate(n)
return [] if n == 1
factor = (2..n).find {|x| n % x == 0}
[factor] + generate(n / factor)
end
Posted at 10:48 PM | Permalink | Comments (3) | TrackBack (1)