Thursday, May 31, 2012

The Single Best Thing For My Career

In 2004 I was working at nelnet - for one of the best bosses I've ever had. I was the lone developer working on NextGen projects, our customers were happy, I was paid well, & I had plenty of vacation - things were good. Truthfully, I felt like I was on top of the world. However, I couldn't help but feel like something was missing. I'd seen a bit of 'how things could be' by reading Refactoring & Extreme Programming Explained. I was 24 years old, and I felt like my current situation wasn't going to allow me to grow much. I'm convinced that if I were older I wouldn't have changed a thing, but I was young, and I dove into the deep-end of consulting.

Like I mentioned, I'd seen a bit of the light before I made the jump. I had read a few good books, I was using Test Driven Development (TDD) and Continuous Integration at work, and I'd seen the (successful) results of my efforts. Before I joined the NextGen team I was working with a larger group of developers, and at one point we were in the middle of delivering a huge release. While everyone else was working weekends I was either not in the office or I was at my desk reading about poker theory. My component of the release worked, 100% of the time. My tests prevented regressions even as the release evolved. Through the many month ordeal, I never once had an issue. I had seen the benefits of TDD, there was no turning back, and I wanted to mature my skills even further.

I joined ThoughtWorks in early 2005. From the moment I walked into the office I felt like I was drinking from a fire-hose. What's firefox? Better than IE, okay then. What's DI? Inject the dependencies so you can use mocks or stubs in the tests, got it. Ruby on Rails, PostgreSQL, I'm on it. Then there's all the other things that aren't technology related, but are still worth knowing: international travel, frequent flyer programs, points credit cards, car rentals, etc etc. In my first year with ThoughtWorks I grew more as a person than I had in the previous 3 years combined.

Obviously, it's not all roses. In my orientation class I remember everyone admitting that they were going to put in 2 years for the experience and get out - I had the same plan. The guy with the most experience made it 6 months, two other guys made it a year, and I stayed for 3. The constant travel can wear on you, but it's worth doing it for as long as you possibly can.

There's three reasons that consulting was like steroids for my career:
  • diversity of experience
  • building my network
  • focus on my craft
Each project of my consulting career was very different: C# in the Travel Industry, VB.net in Insurance, Ruby (& DSLs) in Credit Cards, Ruby on Rails (RoR) in VoIP, RoR in Auctions, RoR in Advertising. Projects often used different operating systems (ms, linux, apple) and databases (mysql, postgres, oracle, sql server). Naturally, I couldn't become an expert in everything I touched, but the exposure gave me views into each world. Additionally, I was able to meet experts from each of those worlds. When I joined ThoughtWorks I was a C# developer carrying a Dell, when I walked out I was carrying a MacBook Air and I was best known in the Ruby space - consulting completely changed the trajectory of my career path.

As I mentioned, I often got the opportunity to meet with various experts. I don't know Oracle well, but if I have a problem, I know I can email Pramod. Even though he never worked there, I met Stu Halloway while working for ThoughtWorks. Stu introduced me to Rich Hickey, which is pretty nice, since I spend the majority of my time working with Clojure these days. I met Martin Fowler and eventually wrote a book with him. My network gets me invited to conferences, allows me to co-organize speakerconf, and when I joined DRW, I brought 13 friends with me. If you believe that "your network determines your net-worth", then the majority of my net-worth is due to what I built while I was consulting.

At speakerconf 2011 I noted that everyone involved in the Software Craftsmanship movement was either a consultant or had very recently left consulting. The question of why this was the case was sent to the twitterverse. Brian Guthrie's response rang very true (it was something along the lines of): as a consultant, our craft is the only thing we own. Consultants hone their craft endlessly. I'm convinced that's the reason that consultants are almost always ahead of the technology curve. As a consultant, to invest in your business is to invest in yourself. If you work for a consultancy you'll not only have the time to focus on your craft, you'll also likely be surrounded by a group of people who have been doing the same - and are happy to show you what they've learned.

These days, I'm no longer at ThoughtWorks. In the end, I couldn't deal with the travel or the business model. However, that doesn't change the fact that it was the single best thing that has ever happened to my career. I would highly recommend consulting to everyone who is truly serious about maturing their software development skills.

note: Not all consultancies seem to require as much travel, Relevance, for example, requires very little travel - from what I hear. I also hear that it's an amazing place to work. It would be near the top of my list if I were to get back into consulting.

Wednesday, May 30, 2012

Is Productivity Killing Your Creativity?

I listen to audio books while I work out. I've been known to leave earworms 'Rapid Italian' on while trying to go to sleep. I read books on trains. I answer emails while eating dinner. I clear out my Google Reader while watching TV with my wife. I wanted to learn Ruby and Blackjack perfect strategy, so I wrote a perfect strategy simulator in Ruby. I'm a multi-tasking machine. I am the world's most productive man, or so I liked to pretend.

About 4 years ago at a conference in São Paulo, Chad Fowler told the audience to delete every feed in their blog reader if they wanted to gain an incredible amount of productivity. I was appalled. I already believe that engineers don't spend nearly enough time staying current, and Chad was telling the audience to spend even less time reading about current events. I had (and have) way too much respect for Chad to call bullshit, but I definitely didn't agree. Then again, I'm aggressive about removing noise from my subscription list, so I chalked up the disagreement to him recommending that the audience remove what were likely very noisy subscription lists anyway - probably a net positive act.

A few years later I found myself on a train from NYC to Greenwich, CT - listening to the Zen and the Art of Motorcycle Maintenance audiobook. The book changed my perspective greatly, and I'll never forget the line: I haven't really had a new idea in years. The following paragraph haunted me as well.
What is in mind is a sort of Chautauqua...that’s the only name I can think of for it...like the traveling tent-show Chautauquas that used to move across America, this America, the one that we are now in, an old-time series of popular talks intended to edify and entertain, improve the mind and bring culture and enlightenment to the ears and thoughts of the hearer. The Chautauquas were pushed aside by faster-paced radio, movies and TV, and it seems to me the change was not entirely an improvement. Perhaps because of these changes the stream of national consciousness moves faster now, and is broader, but it seems to run less deep. The old channels cannot contain it and in its search for new ones there seems to be growing havoc and destruction along its banks. In this Chautauqua I would like not to cut any new channels of consciousness but simply dig deeper into old ones that have become silted in with the debris of thoughts grown stale and platitudes too often repeated. "What’s new?" is an interesting and broadening eternal question, but one which, if pursued exclusively, results only in an endless parade of trivia and fashion, the silt of tomorrow. I would like, instead, to be concerned with the question "What is best?," a question which cuts deeply rather than broadly, a question whose answers tend to move the silt downstream. There are eras of human history in which the channels of thought have been too deeply cut and no change was possible, and nothing new ever happened, and "best" was a matter of dogma, but that is not the situation now. Now the stream of our common consciousness seems to be obliterating its own banks, losing its central direction and purpose, flooding the lowlands, disconnecting and isolating the highlands and to no particular purpose other than the wasteful fulfillment of its own internal momentum. Some channel deepening seems called for.
As I said, Zen and the Art of Motorcycle Maintenance changed my perspective. I began to look back at the last few years of my life, and I felt like my creativity had been stunted. At one point in my life I would stare out the window of a plane for several hours a week pondering whatever technical problem that was troubling me. But, for the last several years I've been being 'productive' by listening to an audiobook or reading something on my iPad. I've been listening, but I haven't been thinking, not deeply.

If you listen to the people around you, they are saying the same things - whether they know it or not. I'm sure at one time you read an article where a CEO swore they were most productive while they were on a plane, unreachable. Just last week a story made the rounds about a company that moved to Hawaii temporarily. In that story they describe how walks on the beach brought a greater understanding, something that probably couldn't have happened while they were all distracted by the day-to-day activities of living in Silicon Valley. And, I now believe that Chad was talking about the same issue - distractions (masked as productive tasks) stealing your creativity.

I'm convinced that my iPhone was the root of my creativity issues. Life is full of 'waiting time' - waiting for the subway, waiting to see your doctor, waiting in the elevator, waiting in line at airport/grocery store/coffee shop, and waiting at the bar to meet your friends. Pre-iPhone I would spend this waiting time pondering anything that was troubling me. Now, I open Safari on my iPhone to see who is the latest injury on the Knicks, who is the lastest football player to sign with FSU, or who's tweeting about what (seems like it's mostly sponsorship requests these days). I don't spend that time thinking about anything, I spend that time reading - reading about things that have very little impact on my life, but seem to always more than fill my waiting time.

At least, that's what I used to do. Now, I've moved anything that can steal my waiting time to the 2nd page on my iPhone. It's no longer taunting me to check twitter, facebook, sports scores, or anything else. My main page allows me to get things done if I need to do them, but it doesn't offer me anything to fill my 'waiting time'. Those apps are just a page away, but not having them staring at me when I instinctually unlock my phone reminds me that I need that time to think, even if it's not deep thinking, I need to 'do' less and 'think' more.

Even this small step has led to better organization in my mind. Now that I'm not 'productive' 100% of the time, I find myself solving issues with more innovation and greater efficiency. The small step and the noticeable improvement have led me to make larger changes - I still multitask as much as I can, but I also set aside time to stare out the window. No agenda, no priorities, just stare and let my mind go wherever it needs to go. After making these changes, I feel better. I have more mental energy to produce innovative solutions at work, and I find that I'm getting things done in a way that leads to greater long-term productivity. My priorities feel right, if you will.

This isn't the kind of thing I usually produce on this blog, but the impact that my changes have made on my life compelled me to share. And, as I already said, if you listen, more and more people are saying the same thing, even if they aren't using the same words. Technology has driven us to greater 'productivity', but often at the cost of deep concentration and thought. Not everyone is okay with that, and more and more people are beginning to push back in their own ways.

Tuesday, May 29, 2012

Clojure: Freezing Time in expectations

Don't do this, this blog post is out-dated. You want this: http://blog.jayfields.com/2012/11/clojure-freezing-time-added-to.html

The current version of expectations (1.4.3) contains support for freezing time within an expectations scenario. I already put this information out in a previous blog entry, and I'm going to use the same examples here.

The following code is a test I was working on at work:
(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (first @fills)))
The test builds a PartialFill domain object, and passes it to handle-fill. The handle-fill fn converts the domain object to a map and conj's the new map onto the fills vector (which is an atom). The above test would fail due to the time not being frozen, and the traditional way to deal with this issue was to change the test to use (in ..) and ignore the :time key-value pair.

The following code would have resulted in a passing test:
(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33} (in (first @fills))))
The above code is fine, as long as you're not interested in verifying the time; however, things get a lot uglier if you do want to verify time. The following code freezes (joda) time, allowing for time verification:
(scenario
 (DateTimeUtils/setCurrentMillisFixed 100)
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 100} (first @fills))
 (DateTimeUtils/setCurrentMillisSystem))
While the above code does result in a passing test, it would cause unexpected issues if expect ever failed. In expectations a failure throws an exception (to terminate scenario execution) and the time reset would never occur. Even if that wasn't the case, the time related code takes away from the actual focus of the test.

Therefore, expectations has been modified to take a :freeze-time parameter as part of a scenario definition. The :freeze-time parameter can be a string or a boolean - when specifying a string, anything parse-able by Joda will do; if you specify a boolean time will simply be stopped at the moment of execution.

With :freeze-time available we can rewrite the above test in the following way:
(scenario
 :freeze-time "2012-4-30"
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (first @fills)))
The resulting code is easier to work with, while still allowing verification of time.

I believe the domain related code does a better job of demonstrating the point; however, the following examples are available if you'd like something simplified.
(scenario
  :freeze-time true
  (expect (DateTime.) (do (Thread/sleep 10) (DateTime.))))

(scenario
  :freeze-time "2012-4-30TZ"
  (expect (.getMillis (DateTime.)) 1335744000000))

Thursday, May 24, 2012

Clojure: expectations, colorized

The current version of expectations (1.4.3) prints colorized results by default on non-windows boxes.

The following screenshot is an example of the output when no tests are failing:


And, the following screenshot is an example of the output when there are failures or errors:


Colorized output is one of those small things that is easy to de-prioritize, but once it's done you can't figure out why you didn't do it earlier. The code to colorize your results is very simple, and there's even a lib, colorize, if you prefer to simply include a dependency instead.

Of course, if you prefer to stick with non-colorized results that's possible as well - simply set the environment variable EXPECTATIONS_COLORIZE to 'false'.

Wednesday, May 23, 2012

Killing IntelliJ Launched Processes

I often use IntelliJ to run applications, and on occasion things go wrong. For example, a thread that wont terminate can cause a running application to become unstoppable via the IntelliJ UI. Usually when this happens I end up running ps aux | grep java and following up with a kill -9 for each process that looks like it might be the one I'm looking for. On good days there's only a few processes; however, things are more complicated when I have several to look through.

Last week I noticed that the command used to launch the process printed in the Console window, and, more importantly, the idea.launcher.port is part of that command: e.g. idea.launcher.port=7538. Assuming the port is unique, or even almost unique it's much easier to ps aux | grep for than java.

Tuesday, May 22, 2012

sidenum function

sidenum function

The sidenum function appends a sign to a number based on a side. The name sidenum was chosen due to it's similarities with signum in spelling and behavior (dealing with signs).

Definition

The sidenum function of a side x and a number y is defined as follows:

note: 'bid' can be replaced with 'buy', and 'ask' can be replaced with 'sell' or 'offer'. The string representation or type of side varies by trading system.

Usage

The sidenum function appears in most trading applications, though not always named 'sidenum'. It can be helpful for both position related math and conserving screen real-estate by displaying 2 concepts (side & quantity) as 1 (a signed quantity).

Examples

Java
    public static int sidenum(String side, int quantity) {
        if (side.equals("ask"))
            return -quantity;
        return quantity;
    }
Clojure
    (defn sidenum [side quantity]
      (if (= side "ask")
        (- quantity)
        quantity))
Ruby
    def sidenum(side, quantity)
      if side == "ask"
        -quantity
      else
        quantity
      end
    end

Thursday, May 17, 2012

jQuery find function

I've recently been using Tilava Table on a few different projects. At one point I ran into a situation where I wanted to change all of the visible rows as well as the template, but the row template was no longer found when using a traditional jQuery selection. The solution to my problem was a combination of holding on to the template as well and using jQuery find to select the td elements I was interested in altering.

In Tilava Table you create a template, but that template row isn't displayed within the table. We could use that as an example, but it seems easier to simplify the example to the following code:
<script src="http://code.jquery.com/jquery-1.7.2.min.js">
</script>
<label><input type="checkbox" id="hideMoreData"/>hide more data</label>
<table id="myTable" style="border:1px solid black">
  <tr class="template">
    <td>row data</td><td class="additional-field">more data</td>
  </tr>
</table>
<button id="addButton">Add</button>
<script>
$(document).ready(function () {
  function addRow(template) {
    $('#myTable').append(template.clone().removeClass('template'));
  }
  var template = $(".template");
  $('#myTable').html("");
  $("#addButton").click(function () {
    addRow(template);
  });
  $("#hideMoreData").click(function () {
    $(".additional-field").toggle(!$('#hideMoreData').is(':checked'));
  });
  addRow(template);
  addRow(template);
});
</script>
The executing version of that code can be found below.

row datamore data


In the above example we have a table in which we can dynamically add rows by clicking the 'Add' button. We also have a checkbox that determines whether or not the additional data is displayed in our table. Click the add button a few times and show and hide the additional data, just to make sure everything is working as you'd expect.

You may have noticed (either by reading the code, or by playing with the example) that when you hide the additional data it hides what is already in the table; however, if you add additional rows the additional data for the new rows will be shown. This is due to the fact that when you select all td elements with the class 'additional-field' the template is not included in the results. Even though the template is not returned in our 'additional-field' selection, it does still exist and is accessible as the var 'template'. When we clone the template var the cloned row will contain the additional-field td, but it will not be 'correctly' toggled.

One solution would be to append the row and rerun the toggle code, but that wouldn't be as efficient - and, jQuery gives us a better solution anyway: find
find: Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
The simple solution is to add the following line in the #hideMoreData click handler.
template.find(".additional-field").toggle(!$('#hideMoreData').is(':checked'));
The finished code is below in it's entirety, and the working version of the code can also be found at the bottom.
<label><input type="checkbox" id="hideMoreData2"/>hide more data</label>
<table id="myTable2" style="border:1px solid black">
  <tr class="template2">
    <td>row data</td><td class="additional-field2">more data</td>
  </tr>
</table>
<button id="addButton2">Add</button>
<script>
$(document).ready(function () {
  function addRow(template) {
    $('#myTable2').append(template.clone().removeClass('template2'));
  }
  var template = $(".template2");
  $('#myTable2').html("");
  $("#addButton2").click(function () {
    addRow(template);
  });
  $("#hideMoreData2").click(function () {
    $(".additional-field2").toggle(!$('#hideMoreData2').is(':checked'));
    template.find(".additional-field2").toggle(!$('#hideMoreData2').is(':checked'));
  });
  addRow(template);
  addRow(template);
});
</script>
row datamore data

Wednesday, May 16, 2012

How I Open Source

It's been recently brought to my attention that I don't view open-source the way that many of my friends do. My attitude has always been:
Here's some code that works well for what I want. If it works well for what you want, great! If not, I'm willing to make changes that improve the library; however, I'm also going to reject any code that causes the library to bloat. Lastly, the library will likely be 'done' in the next few years - at which time I expect it will be mature enough that a stable final release will be possible, or someone will (re)write a superior version. I don't expect an open-source project will ever define what I accomplish in our industry.
It finally occurred to me that others don't think this way when they began telling me that they don't open-source due to -
  • the code isn't mature enough
  • they don't want to write documentation
  • they don't want their time monopolized by feature request emails
I can understand each point of view, but events have occurred in my career that have shaped my (differing) opinion.

I still remember my first open-source project: I didn't tell a soul about it until I had used it in production for over a year and was very confident that I'd addressed the majority of common use cases. It was a .net object relational mapper called NORM, and it was released in 2005. No, you haven't ever heard of it. I polished it for months, and no one cared. After that I never waited to release anything. I now believe that it's highly unlikely that whatever I create will ever gain any traction, so I might as well get it out there, fail quickly, and move on.

No one writes documentation for themselves, they write it for people who they hope will use their software - and very few people people ever gain anything from someone else using their open-source software. That simple equation makes documentation scarce; however, scarce documentation doesn't mean you can't open-source your software, it just means adoption rates will very likely be slowed.

Two years ago I open-sourced expectations with zero documentation, and documentation stayed at zero for at least a year. In that year very few people paid any attention to expectations; however, expectations does fit a sweet spot for some people, and some adoption did occur. Eventually, new adopters began to send pull requests with documentation, and their contributions inspired me to write some documentation of my own. It can be hard to get motivated about providing documentation to theoretical adopters; however, I got my code out there and adoption began, and the motivation came along with those (no longer theoretical) adopters.

If you end up lucky enough to create a project that is widely used, there's no doubt that you'll start getting swamped with email. In the beginning I expect everyone will be overjoyed with their success, and the added workload will be no big deal. However, I imagine over time it begins to feel like a second full-time job, and for what? Developer-fame doesn't get you closer to retirement any faster than watching grass-grow. However, I don't believe that should deter you from putting your work out there. Additionally, I think GitHub has changed the game with respect to moving on. If your project is on GitHub and you decide to call it quits tonight, there will probably be plenty of forks that are more than happy to take your place.

I have no qualms with walking away from projects, as I expect that if the idea is valuable, someone else will be happy to step up and take my place; furthermore, it's more likely that several people will step up and the strongest will survive - which is best for everyone. The best example I've ever seen of this behavior was Capistrano. Jamis Buck famously walked away from Cap in 2009, yet I still know plenty of people using it today without any issue. I firmly believe that if an idea is good, it'll live on even if you've decided you're ready to do something else.

It occurs to me that I might be a bad open-source citizen - releasing way too early and walking away too early as well. If that's the case, then I expect well deserved criticism, but that's just not how I see the world at this point...

Tuesday, May 15, 2012

Agile Development with Clojure

If you've ever spent any time learning Rails then you probably read one of the editions of Agile Web Development with Rails, and if you're like me (skeptical & pedantic) then you probably asked yourself: what the hell does Rails have to do with Agile development? At the time, I assumed that Dave and David were merely capitalizing on the buzz around Agile; however, even if that's the case, I think they did manage to highlight one of my favorite aspects to building websites with Rails: The ability to make a change, reload the page and see the results makes you a much more agile programmer - where 'agile' is defined as: Characterized by quickness, lightness, and ease of movement; nimble

It turns out, it's not very hard to get that same productivity advantage in Clojure as well. I would go so far as to say that the ability to change the server while it's running is assumed if you're using emacs+slime; however, what's not often mentioned is that it's also possible (and trivial) to reload your server code (while it's running) even if you're using IntelliJ, scripts, or anything else.

The majority of the servers I'm working on these days have some type of web UI; therefore, I tie my server side code reloading to a page load. Specifically, each time a websocket is opened the server reloads all of the namespaces that I haven't chosen to ignore. The code below can be found in pretty much every Clojure application that I work on.
(defonce ignored-namespaces (atom #{}))

(defn reload-all []
  (doseq [n (remove (comp @ignored-namespaces ns-name) (all-ns))]
    (require (ns-name n) :reload )))
Like I said, when I open a new websocket, I call (reload-all); however, the (reload-all) fn can be called on any event. When discussing this idea internally at DRW, Joe Walnes pointed out that you could also watch the file system and auto-reload on any changes. That's true, and the important take-away is that you can easily become more productive simply by finding the appropriate hook for what you're working on, and using the code above.

The ignored-namespaces are important for not reloading namespaces that don't ever need to be reloaded (user); other times you'll have a namespace that doesn't behave properly if it's reloaded (e.g. I've found a record + protocol issue in the past, so I don't dynamically reload defrecords in general).

The change-reload webpage-test loop is nice for making changes and seeing the results very quickly - and I strongly prefer it to having to stop and start servers to see new functionality.

Wednesday, May 09, 2012

Follow-up Thoughts on Aligning Business & Programmer Goals

My recent entry on Aligning Business & Programmer Goals led to an email conversation that I thought might be worth sharing.

From A. Nonymous:
I have an issue with tying bonuses to performance due to, basically, performance being out of the programmer's hands. Where I'm working right now developers are treated as code monkeys: We're there to implement features the business people dream up, and nothing more. How could I provide more value when I work in an environment where
  • The visual design phase has already happened (no pushback on any design elements taken seriously)
  • The business development phase has already happened (i.e. the business decides to create a new service tells it's code monkeys: "we need feature x, y, and z")
  • The individual projects we're working on is out of our control.
I can't figure out how to tie what I do to business value in the short term (or even in the year-long-term), because I don't have the autonomy to work on things that I think would benefit the business. I'm forced to do the work that someone else assigns me. Are the new features/bug fixes creating value? Definitely. How much value? Not a clue. How can the value I bring be measured? The only metric I'm seeing is "ease of implementing the next new feature", but the next feature that touches the code I wrote probably won't be developed for months, if not years.

So, it doesn't seem justified to assign me a bonus based on the value that features create, when I have no control over the features.
My response:
Given your context, I wouldn't want a bonus either. I imagine that many people are in the same situation... probably most people.

2 notes-
  • The blog entry isn't solely about bonuses, the ending is all about a visible P&L and nothing about a bonus. I hope people don't completely miss that point.
  • While your position is status quo, I think we should strive to do better as an industry. I don't think it's healthy that we (programmers in general, not necessarily you or me) work for organizations where we know little about the business, don't trust our business counterparts, and are not trusted by our business counterparts. Programmers are often ruthless about measurement and improvement, and wasting that effort on resume driven development or process tweaks is bad for our businesses and our reputations.
It's easy for me to say that the good programmers should quit companies that don't expose a P&L and don't empower their programmers, but I know that personal situations can limit people's choices. At the same time, I feel good about continuing to write about the path that I take, which includes quitting companies that I don't feel are aligned. Even if my ideas can't help you at this point; hopefully, something will come of them in the future.
A. Nonymous followed up with:
I totally get the visible P&L part and agree with you on it. Having more visibility into the inner workings of the business can only help focus effort where it's important. Not every business leader feels that way, unfortunately. I'm currently fighting with my boss about a similar issue. I alluded to it before, but he wants to follow what many consultancies are now calling "Agile":
  • analyst meets with business team
  • analyst comes up with features
  • qa writes acceptance test for features
  • developer receives/estimates/completes features
At no point in this process does the developer have any contact with the business team.

Do you have any idea how I'd go about convincing upper management that sharing the P&L would be a good idea?
And, my final response:
I've never seen studies on the impact of sharing the P&L. Most of the discussion around this stuff is still in it's infancy as far as I can tell. Brian Goetz has been talking about 'language & framework introduction' for awhile, but I've never heard it combined with P&L.

I would attack your situation in one of the two following ways-
  • Fully support your boss' plan, but request to be put on a 'research' or 'experimental' team where you interact directly with the business on a new product (assuming that's possible). I've done this successfully. The rest of IT followed the traditional approach that your boss is proposing, but I broke off with 1 PM and we worked directly with the business on technology for new business lines. I never got to see P&L (though, I never asked), but I would get requirements and deliver features on demand and we ended up constantly impressing the business. e.g. "business: How quickly can you deliver this? (I start thinking of my estimate) business: August? me: (shocked, it was March) I was thinking in the next 2 weeks. -- that conversation actually happened, I'll never forget it. If you get this approved, your boss still gets to do what he wants, and you get to try to do better. If you succeed, he can take the credit for approving the experiment - win/win.
  • Assuming you cannot find a way to interact with the business, focus all your efforts on reducing the delivery cycle. If he's going to force the team to be basically skilled labor, you need to deliver at a rapid enough pace that you fail quickly and pivot toward profitable business goals. You build, they point you in a better direction, you build, they point you in a better direction, on and on. Your 'interaction' with the business is delivering them software, and you will benefit from doing that as often as possible. So, focus on things like choosing the best tools for the job, getting the build time down, reducing the test running time, etc, etc. Anything that's slowing you down from delivering faster, it's in your best interest to remove that, so you can get the business feedback as fast as possible. Even if your boss forced you to only do a roll-out monthly (or worse), the more you have to roll-out, the more feedback you'll get - so removing any thing that takes your time and isn't contributing to feature delivery. Dan Bodart is doing interesting stuff with build times and you should be able to find plenty of advice on speeding up tests - attack that stuff so you deliver as much to the business as possible.
While A. Nonymous may never get to see the P&L, and may never be in a situation where we would want a bonus - there are steps that can be taken to align the business & the programmers.

Monday, May 07, 2012

Clojure: Conditionally Importing

I recently ran into a test that needed (org.joda.time.DateTime.) to always return the same time - so it could easily be asserted against. This situation is fairly common, so it makes sense to add support to expectations. However, I didn't want to force a joda-time dependency on everyone who wanted to use expectations. Luckily, Clojure gives me the ability to conditionally import dependencies.

The test looked something like the following code snippet.
(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (in (first @fills))))
note: build is a fn that creates domain objects (I do a lot of Java Interop).

The test builds a PartialFill domain object, and passes it to handle-fill. The handle-fill fn converts the domain object to a map and conj's the new map onto the fills vector (which is an atom).

The build fn creates a PartialFill who's time is (DateTime.), which means you can either not test the time or you need to freeze the time. Joda time makes it easy to freeze time by calling DateTimeUtils/setCurrentMillisFixed, so you could write the test as:
(scenario
 (DateTimeUtils/setCurrentMillisFixed 100)
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 100} (in (first @fills)))
 (DateTimeUtils/setCurrentMillisSystem))
Of course, that would cause issues if expect ever failed, as failure throws an exception and the time reset would never occur. Even if that wasn't the case, the time related code takes away from what I'm really testing - so I set out to create a declarative approach from within expectations.

The syntax for the freezing time is fairly straightforward, and the original test can be written as what follows (with expectations 1.4.3 & up)
(scenario
 :freeze-time "2012-4-30"
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (in (first @fills))))
Writing the code to freeze time with expectations is trivial enough, but it's only relevant if you actually use joda-time. Since not everyone uses joda-time, it seemed like the best option was to make expectations import joda only if you actually use :freeze-time within your code.

The code linked here shows the conditional imports: https://github.com/jaycfields/expectations/blob/f2a8687/src/clojure/expectations/scenarios.clj#L81-L86

Even if you're not familiar with macros it should be obvious that I'm importing and using the joda-time classes conditionally. At macro expansion time I'm checking for the presence of freeze-time, and if it does exist the necessary classes are imported. If freeze-time is never found, the joda-time classes will never be imported, and no joda-time dependency will need to live within your project.

I always assumed this would be possible, but I'd never actually attempted to put this type of code together. I'm definitely happy with how the code turned out - I can freeze time in expectations if I want, but I don't force anyone to use joda.