<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DE4BQXg9eyp7ImA9WhVbFUQ.&quot;"><id>tag:blogger.com,1999:blog-12467669</id><updated>2012-06-01T20:29:10.663-04:00</updated><category term="RailsConf2007" /><category term="process management" /><category term="books" /><category term="validations" /><category term="hash" /><category term="mocha" /><category term="codeline management" /><category term="contains?" /><category term="gui patterns" /><category term="include" /><category term="array" /><category term="expectations" /><category term="module" /><category term="restarting" /><category term="TDD" /><category term="memfn" /><category term="extension" /><category term="not" /><category term="rss" /><category term="rails" /><category term="unless" /><category term="one assertion per test" /><category term="Testing Refactorings" /><category term="validatable" /><category term="patch" /><category term="poro" /><category term="arbs" /><category term="java" /><category term="anti-pattern" /><category term="singleton class" /><category term="success" /><category term="RailsConf07" /><category term="InfoQ" /><category term="MMS" /><category term="lambda" /><category term="gems" /><category term="method_missing" /><category term="good to great" /><category term="goruco" /><category term="staying current" /><category term="class_eval" /><category term="initial development time" /><category term="code ownership" /><category term="controller" /><category term="interview" /><category term="iPhone" /><category term="state based" /><category term="starting" /><category term="functional testing" /><category term="expressive software" /><category term="innovation" /><category term="unit testing" /><category term="mac os-x" /><category term="parameter" /><category term="static typing" /><category term="principle of least surprise" /><category term="subversion" /><category term="ruby" /><category term="pressure" /><category term="gotham ruby conference" /><category term="blocks" /><category term="podcast" /><category term="ActiveRecord" /><category term="DRY" /><category term="Selenium" /><category term="ActionScript" /><category term="essence" /><category term="eval" /><category term="web sockets" /><category term="sqldsl" /><category term="get-in" /><category term="arrogance" /><category term="Business Natural Language" /><category term="login error" /><category term="language adoption" /><category term="leadership" /><category term="java interop" /><category term="routes" /><category term="creativity" /><category term="apprentice" /><category term="frameworks" /><category term="initialize_with" /><category term="language features" /><category term="split test suite" /><category term="learning" /><category term="if" /><category term="comments" /><category term="application architecture" /><category term="Time.now" /><category term="alias" /><category term="state pattern" /><category term="law of demeter" /><category term="true" /><category term="silverlight" /><category term="RailsConf" /><category term="stub" /><category term="software is art" /><category term="class methods" /><category term="migration" /><category term="erb" /><category term="technical debt" /><category term="jquery" /><category term="literals" /><category term="luminaries" /><category term="Team Size" /><category term="instance_method" /><category term="presenter" /><category term="sql" /><category term="operator precedence" /><category term="unix" /><category term="caller" /><category term="sets" /><category term="master" /><category term="block" /><category term="clojure" /><category term="ampersand" /><category term="erlang" /><category term="Acceptance Tests" /><category term="false" /><category term="define_method" /><category term="functions" /><category term="method" /><category term="open source" /><category term="convention" /><category term="c#" /><category term="test" /><category term="individuals over people" /><category term="library loading" /><category term="DSL" /><category term="TextMate" /><category term="custom assertions" /><category term="macro" /><category term="and" /><category term="strings" /><category term="composing" /><category term="get" /><category term="facets" /><category term="blogs" /><category term="maintainable code" /><category term="xml" /><category term="SMS" /><category term="test names" /><category term="===" /><category term="retrospective" /><category term="refactoring" /><category term="confidence" /><category term="security" /><category term="sequence" /><category term="schema" /><category term="destructuring" /><category term="object" /><category term="Gmail" /><category term="extend" /><category term="RailsConf Europe 2007" /><category term="state" /><category term="flex" /><category term="oracle" /><category term="yaml" /><category term="xUnit" /><category term="pair programming" /><category term="feature toggle" /><category term="dynamic typing" /><category term="journeyman" /><category term="internal DSL" /><category term="to_proc" /><category term="static methods" /><category term="metaclass" /><category term="ria" /><category term="stories" /><category term="testing" /><category term="serialize" /><category term="feature branch" /><category term="ide" /><category term="examples" /><category term="clojure functions" /><category term="Symbol.to_proc" /><category term="simplicity" /><category term="setup" /><category term="javascript" /><category term="forwardable" /><category term="initializer" /><category term="def" /><category term="debugging" /><category term="entrepreneurial programmers" /><category term="mock" /><category term="behavior based" /><category term="ThoughtWorks" /><category term="conference" /><category term="or" /><category term="stack trace" /><category term="mail.app" /><category term="as" /><category term="java classes" /><category term="developers" /><category term="speakerconf" /><category term="agile" /><category term="*" /><category term="screencasts" /><category term="consulting" /><category term="joda" /><category term="class" /><category term="LocalJumpError" /><category term="testing immaturity" /><category term="windows" /><category term="activesupport" /><category term="open" /><category term="mockito" /><category term="testing private methods" /><category term="rake" /><category term="presentations" /><category term="linux" /><category term="upcoming presentations" /><category term="lean" /><category term="dependency injection" /><category term="niche language" /><category term="process" /><category term="railsconfeurope07" /><category term="metaprogramming" /><category term="context" /><category term="blog" /><category term="netnewswire" /><category term="time" /><category term="enumerable" /><category term="symbols" /><category term="disk inventory x" /><category term="IntelliJ" /><category term="inject" /><category term="languages" /><category term="instance_eval" /><category term="dust" /><category term="ddl" /><category term="maps" /><category term="estimation" /><category term="singleton methods" /><title>Jay Fields' Thoughts</title><subtitle type="html">experiences in software development</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.jayfields.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.jayfields.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>506</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/jayfields/mjKQ" /><feedburner:info uri="jayfields/mjkq" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;C0cEQnw7fSp7ImA9WhVbFEU.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-7422254710919667892</id><published>2012-05-31T09:00:00.001-04:00</published><updated>2012-05-31T12:16:43.205-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-31T12:16:43.205-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="developers" /><category scheme="http://www.blogger.com/atom/ns#" term="ThoughtWorks" /><category scheme="http://www.blogger.com/atom/ns#" term="consulting" /><title>The Single Best Thing For My Career</title><content type="html">In 2004 I was working at &lt;a href="http://www.nelnet.com/"&gt;nelnet&lt;/a&gt; - 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, &amp; 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 &lt;a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672"&gt;Refactoring&lt;/a&gt; &amp; &lt;a href="http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0201616416"&gt;Extreme Programming Explained&lt;/a&gt;. 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.
&lt;br&gt;&lt;br&gt;
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 &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test Driven Development&lt;/a&gt; (TDD) and &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration"&gt;Continuous Integration&lt;/a&gt; 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.
&lt;br&gt;&lt;br&gt;
I joined &lt;a href="http://www.thoughtworks.com/"&gt;ThoughtWorks&lt;/a&gt; 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 &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;DI&lt;/a&gt;? Inject the dependencies so you can use mocks or stubs in the tests, got it. &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;, 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.
&lt;br&gt;&lt;br&gt;
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.
&lt;br&gt;&lt;br&gt;
There's three reasons that consulting was like steroids for my career:&lt;ul&gt;
&lt;li&gt;diversity of experience&lt;/li&gt;
&lt;li&gt;building my network&lt;/li&gt;
&lt;li&gt;focus on my craft&lt;/li&gt;&lt;/ul&gt;
Each project of my consulting career was very different: C# in the Travel Industry, VB.net in Insurance, Ruby (&amp; 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.
&lt;br&gt;&lt;br&gt;
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 &lt;a href="http://www.sadalage.com/"&gt;Pramod&lt;/a&gt;. Even though he never worked there, I met &lt;a href="http://thinkrelevance.com/team/members/stuart-halloway"&gt;Stu Halloway&lt;/a&gt; while working for ThoughtWorks. Stu introduced me to &lt;a href="https://twitter.com/#!/richhickey"&gt;Rich Hickey&lt;/a&gt;, which is pretty nice, since I spend the majority of my time working with &lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt; these days. I met &lt;a href="http://martinfowler.com/"&gt;Martin Fowler&lt;/a&gt; and eventually wrote &lt;a href="http://www.amazon.com/Refactoring-Ruby-Edition-Jay-Fields/dp/0321603508"&gt;a book&lt;/a&gt; with him. My network gets me invited to conferences, allows me to co-organize &lt;a href="http://speakerconf.com"&gt;speakerconf&lt;/a&gt;, and when I joined &lt;a href="http://drw.com"&gt;DRW&lt;/a&gt;, 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.
&lt;br&gt;&lt;br&gt;
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. &lt;a href="https://twitter.com/#!/bguthrie"&gt;Brian Guthrie&lt;/a&gt;'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.
&lt;br&gt;&lt;br&gt;
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.
&lt;br&gt;&lt;br&gt;
note: Not all consultancies seem to require as much travel, &lt;a href="http://thinkrelevance.com/"&gt;Relevance&lt;/a&gt;, 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.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-7422254710919667892?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/dUpFOgLcr1U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/7422254710919667892/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=7422254710919667892" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7422254710919667892?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7422254710919667892?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/dUpFOgLcr1U/single-best-thing-for-my-career.html" title="The Single Best Thing For My Career" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/single-best-thing-for-my-career.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE8ERX8-fSp7ImA9WhVbE0U.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-3400041284733864710</id><published>2012-05-30T09:00:00.000-04:00</published><updated>2012-05-30T09:00:04.155-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-30T09:00:04.155-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="creativity" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone" /><category scheme="http://www.blogger.com/atom/ns#" term="innovation" /><title>Is Productivity Killing Your Creativity?</title><content type="html">I listen to audio books while I work out. I've been known to leave &lt;a href="http://www.earwormslearning.com/discover/index.php"&gt;earworms&lt;/a&gt; 'Rapid Italian' on while trying to go to sleep. I read books on trains. I answer emails while eating dinner. I clear out my &lt;a href="http://www.google.com/reader/view/#overview-page"&gt;Google Reader&lt;/a&gt; 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 worlds most productive man, or so I liked to pretend.&lt;br&gt;
&lt;br&gt;
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 &lt;a href="http://blog.jayfields.com/2009/08/staying-current-software-developers.html"&gt;staying current&lt;/a&gt;, 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.&lt;br&gt;
&lt;br&gt;
A few years later I found myself on a train from NYC to Greenwich, CT - listening to the &lt;a href="http://en.wikipedia.org/wiki/Zen_and_the_Art_of_Motorcycle_Maintenance"&gt;Zen and the Art of Motorcycle Maintenance&lt;/a&gt; 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.&lt;blockquote&gt;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.&lt;/blockquote&gt;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.
&lt;br&gt;
&lt;br&gt;
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 &lt;a href="http://blog.simplehoney.com/how-to-build-a-startup-from-a-beach/"&gt;a story&lt;/a&gt; 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.
&lt;br&gt;&lt;br&gt;
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 &lt;a href="http://www.nba.com/knicks/"&gt;Knicks&lt;/a&gt;, who is the lastest football player to sign with &lt;a href="http://www.seminoles.com/"&gt;FSU&lt;/a&gt;, or who's tweeting about what (seems like it's mostly &lt;a href="https://twitter.com/#!/nutrun/status/197656069261103104"&gt;sponsorship requests&lt;/a&gt; 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.&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-3400041284733864710?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/AcRCewKznc0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/3400041284733864710/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=3400041284733864710" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3400041284733864710?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3400041284733864710?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/AcRCewKznc0/is-productivity-killing-your-creativity.html" title="Is Productivity Killing Your Creativity?" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/is-productivity-killing-your-creativity.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEMEQ38_fip7ImA9WhVbEkQ.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-113356539059705437</id><published>2012-05-29T09:00:00.000-04:00</published><updated>2012-05-29T09:00:02.146-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-29T09:00:02.146-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="joda" /><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>Clojure: Freezing Time in expectations</title><content type="html">The current version of expectations (1.4.3) contains support for freezing time within an &lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-scenarios.html"&gt;expectations scenario&lt;/a&gt;. I already put this information out in a &lt;a href="http://blog.jayfields.com/2012/05/clojure-conditionally-importing.html"&gt;previous blog entry&lt;/a&gt;, and I'm going to use the same examples here.
&lt;br&gt;&lt;br&gt;
The following code is a test I was working on at work:
&lt;pre&gt;(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (first @fills)))&lt;/pre&gt;
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 &lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;(in ..)&lt;/a&gt; and ignore the :time key-value pair.
&lt;br&gt;&lt;br&gt;
The following code would have resulted in a passing test:
&lt;pre&gt;(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33} (in (first @fills))))&lt;/pre&gt;
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:
&lt;pre&gt;(scenario
 (DateTimeUtils/setCurrentMillisFixed 100)
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 100} (first @fills))
 (DateTimeUtils/setCurrentMillisSystem))&lt;/pre&gt;
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.
&lt;br&gt;&lt;br&gt;
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 &lt;a href="http://joda-time.sourceforge.net/api-release/org/joda/time/DateTime.html#parse(java.lang.String)"&gt;parse-able&lt;/a&gt; by Joda will do; if you specify a boolean time will simply be stopped at the moment of execution.
&lt;br&gt;&lt;br&gt;
With :freeze-time available we can rewrite the above test in the following way:
&lt;pre&gt;(scenario
 :freeze-time "2012-4-30"
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (first @fills)))&lt;/pre&gt;
The resulting code is easier to work with, while still allowing verification of time.
&lt;br&gt;&lt;br&gt;
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.
&lt;pre&gt;(scenario
  :freeze-time true
  (expect (DateTime.) (do (Thread/sleep 10) (DateTime.))))

(scenario
  :freeze-time "2012-4-30TZ"
  (expect (.getMillis (DateTime.)) 1335744000000))&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-113356539059705437?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/1qPdxtI3VPE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/113356539059705437/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=113356539059705437" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/113356539059705437?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/113356539059705437?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/1qPdxtI3VPE/clojure-freezing-time-in-expectations.html" title="Clojure: Freezing Time in expectations" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/clojure-freezing-time-in-expectations.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMERHY6cCp7ImA9WhVUGEs.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-6237898775794439423</id><published>2012-05-24T09:00:00.000-04:00</published><updated>2012-05-24T09:00:05.818-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-24T09:00:05.818-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>Clojure: expectations, colorized</title><content type="html">The current version of expectations (1.4.3) prints colorized results by default on non-windows boxes.
&lt;br&gt;&lt;br&gt;
The following screenshot is an example of the output when no tests are failing:
&lt;br&gt;&lt;br&gt;
&lt;div class="separator" style="clear: both;"&gt;
&lt;a href="http://3.bp.blogspot.com/-zlr9XXR-uLQ/T6_gdCp5R2I/AAAAAAAAAFw/yHY4UzHsl9I/s1600/Screen%2Bshot%2B2012-05-13%2Bat%2B12.24.38%2BPM.png" imageanchor="1" style=""&gt;&lt;img border="0" height="122" width="400" src="http://3.bp.blogspot.com/-zlr9XXR-uLQ/T6_gdCp5R2I/AAAAAAAAAFw/yHY4UzHsl9I/s400/Screen%2Bshot%2B2012-05-13%2Bat%2B12.24.38%2BPM.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
And, the following screenshot is an example of the output when there are failures or errors:
&lt;br&gt;&lt;br&gt;
&lt;div class="separator" style="clear: both;"&gt;
&lt;a href="http://1.bp.blogspot.com/-nJLOoI3ovps/T6_hHaL6FmI/AAAAAAAAAF8/kTH_w4sVvpQ/s1600/Screen%2Bshot%2B2012-05-13%2Bat%2B12.27.03%2BPM.png" imageanchor="1" style=""&gt;&lt;img border="0" height="196" width="400" src="http://1.bp.blogspot.com/-nJLOoI3ovps/T6_hHaL6FmI/AAAAAAAAAF8/kTH_w4sVvpQ/s400/Screen%2Bshot%2B2012-05-13%2Bat%2B12.27.03%2BPM.png" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
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, &lt;a href="https://github.com/ibdknox/colorize"&gt;colorize&lt;/a&gt;, if you prefer to simply include a dependency instead.
&lt;br&gt;&lt;br&gt;
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'.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-6237898775794439423?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/joS-o4o84ug" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/6237898775794439423/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=6237898775794439423" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6237898775794439423?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6237898775794439423?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/joS-o4o84ug/clojure-expectations-colorized.html" title="Clojure: expectations, colorized" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-zlr9XXR-uLQ/T6_gdCp5R2I/AAAAAAAAAFw/yHY4UzHsl9I/s72-c/Screen%2Bshot%2B2012-05-13%2Bat%2B12.24.38%2BPM.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/clojure-expectations-colorized.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcFRHs7eCp7ImA9WhVUF0o.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-158636305626750444</id><published>2012-05-23T09:00:00.000-04:00</published><updated>2012-05-23T09:00:15.500-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-23T09:00:15.500-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ide" /><category scheme="http://www.blogger.com/atom/ns#" term="IntelliJ" /><category scheme="http://www.blogger.com/atom/ns#" term="process management" /><category scheme="http://www.blogger.com/atom/ns#" term="linux" /><category scheme="http://www.blogger.com/atom/ns#" term="unix" /><title>Killing IntelliJ Launched Processes</title><content type="html">I often use IntelliJ to &lt;a href="http://www.jetbrains.com/idea/webhelp/running-applications.html"&gt;run applications&lt;/a&gt;, 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 &lt;code&gt;ps aux | grep java&lt;/code&gt; and following up with a &lt;code&gt;kill -9&lt;/code&gt; 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.
&lt;br&gt;&lt;br&gt;
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 &lt;code&gt;ps aux | grep&lt;/code&gt; for than java.

&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-hYCtEt6Yc2A/T67uJCIy2AI/AAAAAAAAAFg/S9d1EpKpntY/s1600/idea.launcher.port.png" imageanchor="1" style=""&gt;&lt;img border="0" height="294" width="400" src="http://3.bp.blogspot.com/-hYCtEt6Yc2A/T67uJCIy2AI/AAAAAAAAAFg/S9d1EpKpntY/s400/idea.launcher.port.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-158636305626750444?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/45_XykdYf6M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/158636305626750444/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=158636305626750444" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/158636305626750444?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/158636305626750444?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/45_XykdYf6M/killing-intellij-launched-processes.html" title="Killing IntelliJ Launched Processes" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-hYCtEt6Yc2A/T67uJCIy2AI/AAAAAAAAAFg/S9d1EpKpntY/s72-c/idea.launcher.port.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/killing-intellij-launched-processes.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0EESXc7eCp7ImA9WhVUFkQ.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-7017101190541100404</id><published>2012-05-22T09:00:00.000-04:00</published><updated>2012-05-22T09:00:08.900-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-22T09:00:08.900-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="functions" /><title>sidenum function</title><content type="html">&lt;h1&gt;sidenum function&lt;/h1&gt;
The sidenum function appends a sign to a number based on a side. The name sidenum was chosen due to it's similarities with &lt;a href="http://en.wikipedia.org/wiki/Signum_function"&gt;signum&lt;/a&gt; in spelling and behavior (dealing with signs).
&lt;br&gt;&lt;br&gt;
&lt;div style="font-size:125%;font-weight:bold"&gt;Definition&lt;/div&gt;
&lt;hr&gt;
The sidenum function of a side x and a number y is defined as follows:
&lt;div class="separator" style="clear: both;"&gt;
&lt;a href="http://1.bp.blogspot.com/-535uoHpKbj8/T65W3A7sppI/AAAAAAAAAFE/e-hF5fKNEE0/s1600/sidenum.png" imageanchor="1" style=""&gt;&lt;img border="0" height="60" width="320" src="http://1.bp.blogspot.com/-535uoHpKbj8/T65W3A7sppI/AAAAAAAAAFE/e-hF5fKNEE0/s320/sidenum.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br&gt;
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.
&lt;br&gt;&lt;br&gt;
&lt;div style="font-size:125%;font-weight:bold"&gt;Usage&lt;/div&gt;
&lt;hr&gt;
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 &amp; quantity) as 1 (a signed quantity).
&lt;br&gt;&lt;br&gt;
&lt;div style="font-size:125%;font-weight:bold"&gt;Examples&lt;/div&gt;
&lt;hr&gt;
&lt;span style="font-weight:bold"&gt;Java&lt;/span&gt;&lt;pre&gt;
    public static int sidenum(String side, int quantity) {
        if (side.equals("ask"))
            return -quantity;
        return quantity;
    }&lt;/pre&gt;
&lt;span style="font-weight:bold"&gt;Clojure&lt;/span&gt;&lt;pre&gt;
    (defn sidenum [side quantity]
      (if (= side "ask")
        (- quantity)
        quantity))
&lt;/pre&gt;
&lt;span style="font-weight:bold"&gt;Ruby&lt;/span&gt;&lt;pre&gt;
    def sidenum(side, quantity)
      if side == "ask"
        -quantity
      else
        quantity
      end
    end
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-7017101190541100404?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/Q3DRx5to-v4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/7017101190541100404/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=7017101190541100404" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7017101190541100404?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7017101190541100404?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/Q3DRx5to-v4/sidenum-function.html" title="sidenum function" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-535uoHpKbj8/T65W3A7sppI/AAAAAAAAAFE/e-hF5fKNEE0/s72-c/sidenum.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/sidenum-function.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkAERnwzeSp7ImA9WhVUEkg.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-6980091645152749735</id><published>2012-05-17T08:45:00.000-04:00</published><updated>2012-05-17T08:45:07.281-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-17T08:45:07.281-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="javascript" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><title>jQuery find function</title><content type="html">I've recently been using &lt;a href="https://github.com/stephenbez/tilava-table"&gt;Tilava Table&lt;/a&gt; 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.
&lt;br&gt;&lt;br&gt;
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:&lt;pre&gt;
&amp;lt;script&amp;nbsp;src=&amp;quot;http://code.jquery.com/jquery-1.7.2.min.js&amp;quot;&amp;gt;&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;br/&gt;&amp;lt;label&amp;gt;&amp;lt;input&amp;nbsp;type=&amp;quot;checkbox&amp;quot;&amp;nbsp;id=&amp;quot;hideMoreData&amp;quot;/&amp;gt;hide&amp;nbsp;more&amp;nbsp;data&amp;lt;/label&amp;gt;&lt;br/&gt;&amp;lt;table&amp;nbsp;id=&amp;quot;myTable&amp;quot;&amp;nbsp;style=&amp;quot;border:1px&amp;nbsp;solid&amp;nbsp;black&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;tr&amp;nbsp;class=&amp;quot;template&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;td&amp;gt;row&amp;nbsp;data&amp;lt;/td&amp;gt;&amp;lt;td&amp;nbsp;class=&amp;quot;additional-field&amp;quot;&amp;gt;more&amp;nbsp;data&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/table&amp;gt;&lt;br/&gt;&amp;lt;button&amp;nbsp;id=&amp;quot;addButton&amp;quot;&amp;gt;Add&amp;lt;/button&amp;gt;&lt;br/&gt;&amp;lt;script&amp;gt;&lt;br/&gt;$(document).ready(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;function&amp;nbsp;addRow(template)&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$('#myTable').append(template.clone().removeClass('template'));&lt;br/&gt;&amp;nbsp;&amp;nbsp;}&lt;br/&gt;&amp;nbsp;&amp;nbsp;var&amp;nbsp;template&amp;nbsp;=&amp;nbsp;$(&amp;quot;.template&amp;quot;);&lt;br/&gt;&amp;nbsp;&amp;nbsp;$('#myTable').html(&amp;quot;&amp;quot;);&lt;br/&gt;&amp;nbsp;&amp;nbsp;$(&amp;quot;#addButton&amp;quot;).click(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;&amp;nbsp;&amp;nbsp;});&lt;br/&gt;&amp;nbsp;&amp;nbsp;$(&amp;quot;#hideMoreData&amp;quot;).click(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&amp;quot;.additional-field&amp;quot;).toggle(!$('#hideMoreData').is(':checked'));&lt;br/&gt;&amp;nbsp;&amp;nbsp;});&lt;br/&gt;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;});&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
The executing version of that code can be found below.&lt;br&gt;&lt;br&gt;
&lt;script src="http://code.jquery.com/jquery-1.7.2.min.js"&gt;
&lt;/script&gt;
&lt;label&gt;&lt;input type="checkbox" id="hideMoreData"/&gt;hide more data&lt;/label&gt;
&lt;table id="myTable" style="border:1px solid black"&gt;
  &lt;tr class="template"&gt;
    &lt;td&gt;row data&lt;/td&gt;&lt;td class="additional-field"&gt;more data&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;button id="addButton"&gt;Add&lt;/button&gt;
&lt;script&gt;
$(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);
});
&lt;/script&gt;&lt;br&gt;&lt;br&gt;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.
&lt;br&gt;&lt;br&gt;
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.
&lt;br&gt;&lt;br&gt;
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: &lt;a href="http://api.jquery.com/find/"&gt;find&lt;/a&gt;&lt;br&gt;&lt;blockquote&gt;find: Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.&lt;/blockquote&gt;The simple solution is to add the following line in the #hideMoreData click handler.&lt;pre&gt;
template.find(".additional-field").toggle(!$('#hideMoreData').is(':checked'));&lt;/pre&gt;The finished code is below in it's entirety, and the working version of the code can also be found at the bottom.
&lt;pre&gt;
&amp;lt;label&amp;gt;&amp;lt;input&amp;nbsp;type=&amp;quot;checkbox&amp;quot;&amp;nbsp;id=&amp;quot;hideMoreData2&amp;quot;/&amp;gt;hide&amp;nbsp;more&amp;nbsp;data&amp;lt;/label&amp;gt;&lt;br/&gt;&amp;lt;table&amp;nbsp;id=&amp;quot;myTable2&amp;quot;&amp;nbsp;style=&amp;quot;border:1px&amp;nbsp;solid&amp;nbsp;black&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;tr&amp;nbsp;class=&amp;quot;template2&amp;quot;&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;td&amp;gt;row&amp;nbsp;data&amp;lt;/td&amp;gt;&amp;lt;td&amp;nbsp;class=&amp;quot;additional-field2&amp;quot;&amp;gt;more&amp;nbsp;data&amp;lt;/td&amp;gt;&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/tr&amp;gt;&lt;br/&gt;&amp;lt;/table&amp;gt;&lt;br/&gt;&amp;lt;button&amp;nbsp;id=&amp;quot;addButton2&amp;quot;&amp;gt;Add&amp;lt;/button&amp;gt;&lt;br/&gt;&amp;lt;script&amp;gt;&lt;br/&gt;$(document).ready(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;function&amp;nbsp;addRow(template)&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$('#myTable2').append(template.clone().removeClass('template2'));&lt;br/&gt;&amp;nbsp;&amp;nbsp;}&lt;br/&gt;&amp;nbsp;&amp;nbsp;var&amp;nbsp;template&amp;nbsp;=&amp;nbsp;$(&amp;quot;.template2&amp;quot;);&lt;br/&gt;&amp;nbsp;&amp;nbsp;$('#myTable2').html(&amp;quot;&amp;quot;);&lt;br/&gt;&amp;nbsp;&amp;nbsp;$(&amp;quot;#addButton2&amp;quot;).click(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;&amp;nbsp;&amp;nbsp;});&lt;br/&gt;&amp;nbsp;&amp;nbsp;$(&amp;quot;#hideMoreData2&amp;quot;).click(function&amp;nbsp;()&amp;nbsp;{&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(&amp;quot;.additional-field2&amp;quot;).toggle(!$('#hideMoreData2').is(':checked'));&lt;br/&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;template.find(&amp;quot;.additional-field2&amp;quot;).toggle(!$('#hideMoreData2').is(':checked'));&lt;br/&gt;&amp;nbsp;&amp;nbsp;});&lt;br/&gt;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;&amp;nbsp;&amp;nbsp;addRow(template);&lt;br/&gt;});&lt;br/&gt;&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;label&gt;&lt;input type="checkbox" id="hideMoreData2"/&gt;hide more data&lt;/label&gt;
&lt;table id="myTable2" style="border:1px solid black"&gt;
  &lt;tr class="template2"&gt;
    &lt;td&gt;row data&lt;/td&gt;&lt;td class="additional-field2"&gt;more data&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;button id="addButton2"&gt;Add&lt;/button&gt;
&lt;script&gt;
$(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);
});
&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-6980091645152749735?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/FJN2uWJIEI8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/6980091645152749735/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=6980091645152749735" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6980091645152749735?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6980091645152749735?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/FJN2uWJIEI8/jquery-find-function.html" title="jQuery find function" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/jquery-find-function.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UBRHk4fSp7ImA9WhVUEUQ.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-215052714205071362</id><published>2012-05-16T15:07:00.000-04:00</published><updated>2012-05-16T15:07:35.735-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-16T15:07:35.735-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><title>How I Open Source</title><content type="html">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: &lt;blockquote&gt;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.&lt;/blockquote&gt;
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 - &lt;ul&gt;&lt;li&gt;the code isn't mature enough&lt;li&gt;they don't want to write documentation&lt;li&gt;they don't want their time monopolized by feature request emails&lt;/ul&gt;I can understand each point of view, but events have occurred in my career that have shaped my (differing) opinion. &lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
Two years ago I open-sourced &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; 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.&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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 &lt;a href="http://en.wikipedia.org/wiki/Capistrano"&gt;Capistrano&lt;/a&gt;. 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.&lt;br&gt;&lt;br&gt;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...&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-215052714205071362?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/00NyqY5rrUg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/215052714205071362/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=215052714205071362" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/215052714205071362?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/215052714205071362?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/00NyqY5rrUg/how-i-open-source.html" title="How I Open Source" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>5</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/how-i-open-source.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0cAQXg4fip7ImA9WhVUEEU.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-4757835528319951042</id><published>2012-05-15T07:24:00.000-04:00</published><updated>2012-05-15T07:24:00.636-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-15T07:24:00.636-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>Agile Development with Clojure</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://ecx.images-amazon.com/images/I/51lbXFwhFvL._SL500_AA300_.jpg" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="300" width="300" src="http://ecx.images-amazon.com/images/I/51lbXFwhFvL._SL500_AA300_.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;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 &amp; pedantic) then you probably asked yourself: what the hell does Rails have to do with &lt;a href="http://agilemanifesto.org/"&gt;Agile&lt;/a&gt; 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&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;pre&gt;(defonce ignored-namespaces (atom #{}))

(defn reload-all []
  (doseq [n (remove (comp @ignored-namespaces ns-name) (all-ns))]
    (require (ns-name n) :reload )))&lt;/pre&gt;

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.&lt;br&gt;
&lt;br&gt;
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).&lt;br&gt;
&lt;br&gt;
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.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-4757835528319951042?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/PFUUqNM9LhA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/4757835528319951042/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=4757835528319951042" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/4757835528319951042?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/4757835528319951042?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/PFUUqNM9LhA/agile-development-with-clojure.html" title="Agile Development with Clojure" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/agile-development-with-clojure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYFRHo9eSp7ImA9WhVVFUQ.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-6716907026878006878</id><published>2012-05-09T16:41:00.000-04:00</published><updated>2012-05-09T16:41:55.461-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-09T16:41:55.461-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="entrepreneurial programmers" /><category scheme="http://www.blogger.com/atom/ns#" term="speakerconf" /><title>Follow-up Thoughts on Aligning Business &amp; Programmer Goals</title><content type="html">My recent entry on &lt;a href="http://blog.jayfields.com/2012/02/aligning-business-programmer-goals.html"&gt;Aligning Business &amp; Programmer Goals&lt;/a&gt; led to an email conversation that I thought might be worth sharing.
&lt;br&gt;&lt;br&gt;
From A. Nonymous:&lt;blockquote&gt;
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&lt;ul&gt;&lt;li&gt;The visual
design phase has already happened (no pushback on any design elements
taken seriously)&lt;li&gt; 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")
&lt;li&gt;The individual
projects we're working on is out of our control.&lt;/ul&gt;

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.
&lt;br&gt;&lt;br&gt;
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.&lt;/blockquote&gt;My response:&lt;blockquote&gt;
Given your context, I wouldn't want a bonus either. I imagine
that many people are in the same situation... probably most people.
&lt;br&gt;&lt;br&gt;
2 notes-&lt;ul&gt;
&lt;li&gt;The blog entry isn't solely about bonuses, the ending is all about a
visible P&amp;L and nothing about a bonus. I hope people don't completely
miss that point.
&lt;li&gt;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.&lt;/ul&gt;

It's easy for me to say that the good programmers should quit
companies that don't expose a P&amp;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.&lt;/blockquote&gt;
A. Nonymous followed up with:&lt;blockquote&gt;I totally get the visible P&amp;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":&lt;ul&gt;&lt;li&gt;analyst meets with business team
&lt;li&gt;analyst comes up with features&lt;li&gt;qa writes acceptance test for features
&lt;li&gt;developer receives/estimates/completes features&lt;/ul&gt;At no point
in this process does the developer have any contact with the business
team.
&lt;br&gt;&lt;br&gt;
Do you have any idea how I'd go about convincing
upper management that sharing the P&amp;L would be a good idea? &lt;/blockquote&gt;And, my final response:&lt;blockquote&gt;
I've never seen studies on the impact of sharing the P&amp;L. Most of the
discussion around this stuff is still in it's infancy as far as I can
tell. Brian Goetz has been &lt;a href="http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=5551016"&gt;talking&lt;/a&gt; about 'language &amp; framework
introduction' for awhile, but I've never heard it combined with P&amp;L.
&lt;br&gt;&lt;br&gt;
I would attack your situation in one of the two following ways-&lt;ul&gt;
&lt;li&gt;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&amp;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.
&lt;li&gt;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 &lt;a href="http://dan.bodar.com/2012/02/28/crazy-fast-build-times-or-when-10-seconds-starts-to-make-you-nervous/"&gt;interesting stuff with build
times&lt;/a&gt; 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.&lt;/ul&gt;&lt;/blockquote&gt;While A. Nonymous may never get to see the P&amp;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 &amp; the programmers.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-6716907026878006878?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/OeGz50fK_Zw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/6716907026878006878/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=6716907026878006878" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6716907026878006878?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6716907026878006878?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/OeGz50fK_Zw/follow-up-thoughts-on-aligning-business.html" title="Follow-up Thoughts on Aligning Business &amp; Programmer Goals" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/follow-up-thoughts-on-aligning-business.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YCRnY5fyp7ImA9WhVVFEk.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-401436077584822924</id><published>2012-05-07T18:55:00.002-04:00</published><updated>2012-05-07T21:39:27.827-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-07T21:39:27.827-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><title>Clojure: Conditionally Importing</title><content type="html">I recently ran into a test that needed (org.joda.time.&lt;a href="http://joda-time.sourceforge.net/api-release/org/joda/time/DateTime.html"&gt;DateTime&lt;/a&gt;.) 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 &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt;. 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.&lt;br&gt;
&lt;br&gt;
The test looked something like the following code snippet.&lt;br&gt;
&lt;pre&gt;
(scenario
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (in (first @fills))))&lt;/pre&gt;

note: build is a fn that creates domain objects (I do a lot of Java Interop).&lt;br&gt;
&lt;br&gt;
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).&lt;br&gt;
&lt;br&gt;
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:&lt;br&gt;
&lt;pre&gt;
(scenario
 (DateTimeUtils/setCurrentMillisFixed 100)
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 100} (in (first @fills)))
 (DateTimeUtils/setCurrentMillisSystem))&lt;/pre&gt;

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.&lt;br&gt;
&lt;br&gt;
The syntax for the freezing time is fairly straightforward, and the original test can be written as what follows (with expectations 1.4.3 &amp; up)&lt;br&gt;
&lt;pre&gt;
(scenario
 :freeze-time "2012-4-30"
 (handle-fill (build PartialFill))
 (expect {:px 10 :size 33 :time 1335758400000} (in (first @fills))))&lt;/pre&gt;

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.&lt;br&gt;
&lt;br&gt;
The code linked here shows the conditional imports: &lt;a href="https://github.com/jaycfields/expectations/blob/f2a8687/src/clojure/expectations/scenarios.clj#L81-L86"&gt;https://github.com/jaycfields/expectations/blob/f2a8687/src/clojure/expectations/scenarios.clj#L81-L86&lt;/a&gt;&lt;br&gt;
&lt;br&gt;
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.&lt;br&gt;
&lt;br&gt;
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.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-401436077584822924?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/Z-MmbtWzh5k" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/401436077584822924/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=401436077584822924" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/401436077584822924?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/401436077584822924?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/Z-MmbtWzh5k/clojure-conditionally-importing.html" title="Clojure: Conditionally Importing" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>6</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/05/clojure-conditionally-importing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUDR3k_fSp7ImA9WhVXEE8.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-3747032706480120215</id><published>2012-04-09T22:14:00.001-04:00</published><updated>2012-04-09T22:17:56.745-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-09T22:17:56.745-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="java interop" /><title>Eval a Clojure Snippet in Java</title><content type="html">The vast majority of the existing code for my current project is in Java, but the majority of the new code that we write is in Clojure; as a result, I spend quite a bit of time bouncing between Clojure &amp; Java. Recently I was working with some Clojure code that was easily testable (in isolation) within Clojure, but was also executed as part of some higher-level Java integration tests. Within the Clojure tests I used &lt;a href="http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/with-redefs"&gt;with-redefs&lt;/a&gt; to temporarily set the state of a few atoms I cared about; however, setting the same state within the higher-level Java tests turned out to be a bit more of an interesting task.
&lt;br&gt;&lt;br&gt;
Our Java integration tests, like all integration tests I've ever run into, look simple enough when looking at the code of the test; however, if you want to understand how the simple test exercises several collaborations you'll need to dig deep into the bowels of the integration test helpers. Integration test helpers are usually a very fragile combination of classes that stub behavior, classes with preloaded fake datasets, and classes that provide event triggering hooks. In my experience you also generally need the &lt;a href="http://blog.jayfields.com/2011/07/high-level-test-whisperer.html"&gt;High-Level Test Whisperer&lt;/a&gt; on hand to answer any questions, and you generally don't want to make any changes without asking for the HLTW's assistance.
&lt;br&gt;&lt;br&gt;
Today's experience went exactly as I expect all of my experiences with the functional tests to go:
&lt;br&gt;&lt;br&gt;
I added a bit of behavior, and tested everything that I thought was worth testing. With all the new tests passing, I ran the entire suite - 85 errors. The nice thing about errors in functional tests: they generally cause all of the functional tests to break in an area of code that you know you weren't working in. I wasn't that surprised that so many tests had broken, I was adding a bit of code into a core area of our application. However, I wasn't really interested in testing my new behavior via the functional tests, so I quickly looked for the easiest thing I could do to make the functional tests unaware of my change. The solution was simple enough, conceptually, the new code only ran if specific values were set within the atom in my new namespace; therefore, all I needed to do was clear that atom before the functional tests were executed.
&lt;br&gt;&lt;br&gt;
&lt;a href="http://blog.jayfields.com/2009/05/calling-clojure-from-java.html"&gt;Calling Clojure from Java&lt;/a&gt; is easy, so I set out to grab the atom from the Clojure and swap! + dissoc the values I cared about. Getting the atom in Java was simple enough, and clojure.lang.Atom has a swap() method, but it takes an IFn. I spent a few minutes looking at passing in a IFn that dissoc'd correctly for me; however, when nothing was painfully obvious I took a step back and considered an easier solution: eval.
&lt;br&gt;&lt;br&gt;
As I previously mentioned, this is not only test code, but it's test code that I already expect to take a bit longer to run**. Given that context, eval seemed like an easy choice for solving my issue. The following code isn't pretty, but it got done exactly what I was looking for.&lt;pre&gt;RT.var("clojure.core", "eval").invoke(
  RT.var("clojure.core","read-string").invoke("(swap! my-ns/my-atom dissoc :a-key)"));&lt;/pre&gt;I wasn't done yet, as I still needed the HLTW's help on getting everything to play nice together; however, that little Clojure snippet got me 90% of what I needed to get my behavior out of the way of the existing functional tests. I wouldn't recommend doing something like that in prod, but for what I needed it worked perfectly.
&lt;br&gt;&lt;br&gt;
** let's not get carried away, the entire suite still runs in 20 secs. That's not &lt;a href="http://dan.bodar.com/2012/02/28/crazy-fast-build-times-or-when-10-seconds-starts-to-make-you-nervous/"&gt;crazy fast&lt;/a&gt;, but I'm satisfied for now.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-3747032706480120215?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/Bq2l6RbVNHQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/3747032706480120215/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=3747032706480120215" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3747032706480120215?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3747032706480120215?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/Bq2l6RbVNHQ/eval-clojure-snippet-in-java.html" title="Eval a Clojure Snippet in Java" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/04/eval-clojure-snippet-in-java.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUFQ3k6cSp7ImA9WhVTGEU.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-1308975957802602171</id><published>2012-03-04T12:40:00.000-05:00</published><updated>2012-03-04T12:46:52.719-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-03-04T12:46:52.719-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="application architecture" /><title>When to Break Apart your Application</title><content type="html">At speakerconf Aruba 2012, &lt;a href="http://www.innoq.com/blog/st/"&gt;Stefan Tilkov&lt;/a&gt; presented on the topic of building Systems. One aspect of his presentation that really struck a chord with me was around the competing desires while designing and delivering an application. This blog entry is about summarizing that aspect and what I took away from it.**&lt;br&gt;
&lt;br&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://1.bp.blogspot.com/-Wnsjgx4SCpU/T1OcQ6wgWZI/AAAAAAAAADA/gHvV9ZxMvDY/s1600/Screen%2Bshot%2B2012-03-04%2Bat%2B11.44.20%2BAM.png" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="236" width="320" src="http://1.bp.blogspot.com/-Wnsjgx4SCpU/T1OcQ6wgWZI/AAAAAAAAADA/gHvV9ZxMvDY/s320/Screen%2Bshot%2B2012-03-04%2Bat%2B11.44.20%2BAM.png" /&gt;&lt;/a&gt;&lt;/div&gt;
Stefan noted that early in an application's life, a monolithic application is far easier to deliver; however, as the application grows, what is important begins to change. The slide to the right shows how priorities can change over time.&lt;br&gt;
&lt;br&gt;
While starting with a monolithic application is easier to deliver, that application often grows to an unmaintainable size - i.e. the vast majority of the time we boil the frog. Stefan argued that the benefits are so obviously there that for an application of any moderate size it's more efficient to bite the bullet and break things up (where logical) in the beginning.&lt;br&gt;
&lt;br&gt;
I'm guessing the vast majority of the applications that Stefan and innoQ encounter probably began knowing that they would grow to significant size and would have benefited from any type of modularization, but my working environment is a bit different. When I start something new for a trader at DRW -
&lt;ul&gt;
&lt;li&gt;the project may never grow to the unmaintainable size 
&lt;li&gt;if it does, it's likely that I'll want things broken up in a way that I couldn't have anticipated when the project began.&lt;/ul&gt;
Given my context, I couldn't conclude that things should be broken up immediately, but I didn't want to ignore the point that Stefan was making about goals changing over time.&lt;br&gt;&lt;br&gt;

&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://2.bp.blogspot.com/-K3GgpswGy_U/T1OlKqFenCI/AAAAAAAAADM/HQ_je8z05Yg/s1600/Screen%2Bshot%2B2012-03-04%2Bat%2B12.22.56%2BPM.png" imageanchor="1" style="clear:right; float:right; margin-left:1em; margin-bottom:1em"&gt;&lt;img border="0" height="180" width="320" src="http://2.bp.blogspot.com/-K3GgpswGy_U/T1OlKqFenCI/AAAAAAAAADM/HQ_je8z05Yg/s320/Screen%2Bshot%2B2012-03-04%2Bat%2B12.22.56%2BPM.png" /&gt;&lt;/a&gt;&lt;/div&gt;

Stefan also had a slide about Modularization (also on the right) that gave numbers and recommendations on how to break things up. This slide reminded me of Matt Deiters' presentation at speakerconf Rome 2011. Matt told us about his experience teaching kids in Africa to program. While teaching programming, Matt observed that giving the children the single rule that they shouldn't have methods longer than 10 lines resulted in the children writing code with &lt;a href="http://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;SRP&lt;/a&gt;, encapsulation, and other positive attributes. Matt argued that beginning with a simple rule that could get you 80% of what you want is likely better than 10 rules that, if followed correctly, could get you 100% of what you want.&lt;br&gt;
&lt;br&gt;
I applied Matt's idea to Stefan's talk and suggested that perhaps the easiest thing was to take Stefan's modularization table and strictly adhere to changing things when LOC thresholds are broken. This would allow you to quickly deliver a monolithic application early in the development process, while avoiding boiling the frog in the future - once you cross the agreed upon magic number (LoC) then you aggressively address breaking the application into smaller and independent modules/processes/components/projects/whatever.&lt;br&gt;&lt;br&gt;
This strategy gives a nod to the fact that priorities do change as an application evolves and it's best to evolve the application along with those priorities, but not when it's too late. I plan to try this on my next greenfield application.
&lt;br&gt;&lt;br&gt;
** I'm going from memory, so I could be slightly off. Don't hold Stefan accountable for my interpretation of his ideas.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-1308975957802602171?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/H-ewo5XKepo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/1308975957802602171/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=1308975957802602171" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/1308975957802602171?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/1308975957802602171?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/H-ewo5XKepo/when-to-break-apart-your-application.html" title="When to Break Apart your Application" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-Wnsjgx4SCpU/T1OcQ6wgWZI/AAAAAAAAADA/gHvV9ZxMvDY/s72-c/Screen%2Bshot%2B2012-03-04%2Bat%2B11.44.20%2BAM.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/03/when-to-break-apart-your-application.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkMAQHs8cCp7ImA9WhVTFE8.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-7463993852199775760</id><published>2012-02-28T06:27:00.000-05:00</published><updated>2012-02-28T06:27:21.578-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-28T06:27:21.578-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="entrepreneurial programmers" /><category scheme="http://www.blogger.com/atom/ns#" term="speakerconf" /><title>Aligning Business &amp; Programmer Goals</title><content type="html">A theme emerged, last week at &lt;a href="http://speakerconf.com"&gt;speakerconf&lt;/a&gt;, around the idea that programmers and businesses still don't seem to be on the same page. I'm not sure if it came up even earlier, but for me it began after I gave a presentation on Lessons Learned Adopting Clojure. The talk is similar to &lt;a href="http://blog.jayfields.com/2012/01/lessons-learned-while-introducing-new.html"&gt;Lessons Learned while Introducing a New Programming Language&lt;/a&gt;, but focuses more on the technical aspects of selecting Clojure. Upon talk completion the first question I got was from &lt;a href="http://www.briangoetz.com/"&gt;Brian Goetz&lt;/a&gt;, something along the lines of: &lt;blockquote&gt;
(Brian) So, you wanted to use another language and simply introduced it into production?&lt;br&gt;
(me) More or less.&lt;br&gt;
(Brian) Do you think that's the most effective way for organizations to select languages or frameworks?
&lt;/blockquote&gt;The first response to the question was the obvious and appropriate one:&lt;blockquote&gt;That sounds better than a CTO making a decision while drunk and on a golf-course with a vendor. -- &lt;a href="http://grahamis.com/blog/"&gt;Josh Graham&lt;/a&gt;&lt;/blockquote&gt;Of course, I agree with that response, but that doesn't mean that my choice was the best answer either. The question is actually something I've been struggling with for awhile. At speakerconf Aruba 2011, Lyle (&lt;a href="http://drw.com"&gt;DRW&lt;/a&gt; CTO) gave a presentation on how we do things at DRW. One of Lyle's slides listed each language that we use in production at DRW. There were a lot of languages, probably around 25, and &lt;a href="http://davethomas.net/"&gt;Dave Thomas&lt;/a&gt; questioned whether it was a positive or a maintenance nightmare. The answer today is the same as it was then, so far the ROI is positive - but, I couldn't help feeling uneasy about the situation.&lt;br&gt;
&lt;br&gt;
I answered somewhat noncommittally, along the lines of: While selecting a language, I chose one that I felt anyone in DRW could easily learn and use. Additionally, I selected a language that aligned with our business goals of developing software quickly and delivering applications that performed at the speeds required by the traders. And, if not the man on the ground, who is better suited for selecting languages to introduce into production?&lt;br&gt;
&lt;br&gt;Brian followed up with:
&lt;blockquote&gt;The point I was trying to get at was: how does this scale?  If every developer chooses what is best for them, is that necessarily best for the organization?  How do we balance between the business value of increasing individual productivity and the business value of reducing maintenance costs or development risk?  If we have to make compromises to achieve better scale, who should make these decisions, and how?&lt;br&gt;
&lt;br&gt;
The usual developer argument -- "this is more productive for me, and increasing my productivity is good for the company" is good as far as it goes, but most developers incorrectly assume these interests are 100% aligned when they are in reality probably only merely overlapping.&lt;/blockquote&gt;
These are truly great questions, and no one had a great response. The discussion basically died off as we pondered the tough answers to these questions.&lt;br&gt;
&lt;br&gt;
At lunch the next day I asked Brian how he thought companies should go about selecting languages, and I think his response was spot-on (again, going from memory, so likely misquoting): I'm not sure, but I sure do see a lot of developers introducing whatever language or framework they want, leaving within a year, and sticking their employer with a big maintenance problem. The same can be said for consultants. They often show up advocating a sexy new language, mostly deliver a new application and then leave the company with an application that can't be maintained by the existing full-time staff.&lt;br&gt;
&lt;br&gt;
Someone quickly spoke up, stating that Resume Driven Development has been going on for years. I can't argue with that, but I did have a different perspective.&lt;br&gt;
&lt;br&gt;
A few years ago I joined DRW, and a substantial portion of my salary became bonus money. I had previously worked in places where getting bonus money was about as likely as meeting your future wife at a JavaOne, so I was obviously skeptical. Luckily, I had a friend who already worked for DRW; he reassured me that, in finance, having a bonus as part of your compensation was common, and so was it actually getting paid. I took the job, and instantly my performance at work was more directly tied to my income. I'm sure this reality influenced my decision making, and I witnessed the difference in the decisions my colleagues were making. When people spoke about using/upgrading frameworks/libraries, we didn't talk about what was cool, we discussed the risks of breaking things, the outstanding bugs with the framework/lib, and the immediate value that the inclusion would provide.&lt;br&gt;
&lt;br&gt;
Given this experience, I advocated to the other speakerconf presenters that bonuses are a potential solution to combatting Resume Driven Development. The other speakerconf presenters gave me the same response I generally get when advocating bonuses for programmers: That works for you and your trading firm, but most people can't directly tie what they do to how the business makes money. I'm always skeptical of this response for 2 reasons: The business must be able to track whether or not their new idea is making them money, or, if the new software isn't targeted to an external customer, then it should be able to be rated by internal customers. Also, shouldn't the CTO or the Director of Software be able to make an informed bonus decision by either directly interacting with the teams, or trusting the feedback of the managers of those teams?&lt;br&gt;
&lt;br&gt;
I guess, if you can't tie what you are doing to the value it's providing the company, and you don't trust your managers to give an accurate assessment, building your resume is probably the best use of your time... and the results aren't very surprising... but, I digress.&lt;br&gt;
&lt;br&gt;
I gave the same response I've given a few dozen times: I just don't buy it. You should be able to see how many new customers your new website generated, or less cancellations, new reservations, policy efficiency, auction traffic, add-on sales purchased, etc. Each of the projects I've worked on for the past 8 years could generate metrics that I would have been happy to tie to a bonus. In our back-office, it's harder to tie the value of a back office system to an individual trade; however, we can capture metrics on uptime, latency, and any other attribute that we deem important to the internal systems that are customers of the back office systems.&lt;br&gt;
&lt;br&gt;
If you want to measure your value and you trust your management, receiving part of your salary as a bonus is completely viable.&lt;br&gt;
&lt;br&gt;
Aligning the goals of the business and the programmers came up again when the afternoon presentations began. Joshua Kerievsky is looking to tackle this same issue at Industrial Logic. Joshua's presentation discussed bridging the gap between managers and programmers, which is a more general version of the same conversation we'd been having about language adoption. Bridging this gap is something he's begun focusing on at Industrial Logic. The benefits are obvious for customers, they should receive higher quality and more relevant software. However, it's not just about customers, the programmers will be more effective and should also have higher job satisfaction rates. No one likes being stuck on yet another, doomed to fail, misguided project using outdated technology. Both parties should benefit by unifying the goals of the programmer and the business.&lt;br&gt;
&lt;br&gt;
Josh knew my opinion on using bonuses to help align both parties; however, he and I both incorrectly assumed that the developers at &lt;a href="http://www.forward.co.uk/"&gt;Forward&lt;/a&gt; also received business performance based bonuses. I believe that they have some things in the works and situations can vary, but Fred George made it clear that in general a bonus is not tied to business focus. That's fairly standard, but what makes Forward unique is that the business works very, very closely with the programmers. I think Fred nicely summed up the situation: Our programmers are gamers, and we show them the &lt;a href="http://en.wikipedia.org/wiki/Income_statement"&gt;P&amp;L&lt;/a&gt;. They want to see that number grow, even if it doesn't directly deposit in their account.&lt;br&gt;
&lt;br&gt;
Fred's statement rang very true to me. I've had jobs in the past where I didn't even have a bonus, but the ones that I felt most dedicated to were the ones where I was given visibility into the business. If I can't see the outcome of my programming choices to the business, I'll have a much harder time iterating towards success - worse, I won't even know at what level we are failing and I'll lack the motivation that the failure would provide me.&lt;br&gt;
&lt;br&gt;
At the end of the day, programmers like to measure and improve. If you give them a P&amp;L and let them know that it's the most important thing, they'll do whatever they can to help. If you don't give them a P&amp;L they'll focus on making other metrics more efficient. This may include using new technologies to inspire more blog entires, spending hours unnecessarily tweaking the development process, or any other way of measuring their "success." The answer is obvious, let the programmers measure success the same way the business does, using the bottom line. What's not always obvious is how to properly share that information; however, all the discussion at speakerconf definitely convinced me that it's worth solving this issue even if it requires a bit of extra effort.&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-7463993852199775760?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/zoNHL-RH_BU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/7463993852199775760/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=7463993852199775760" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7463993852199775760?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/7463993852199775760?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/zoNHL-RH_BU/aligning-business-programmer-goals.html" title="Aligning Business &amp; Programmer Goals" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>5</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/02/aligning-business-programmer-goals.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4NR346eyp7ImA9WhVTGEo.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-3166506696534781620</id><published>2012-02-15T09:30:00.000-05:00</published><updated>2012-03-04T11:36:36.013-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-03-04T11:36:36.013-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="conference" /><category scheme="http://www.blogger.com/atom/ns#" term="speakerconf" /><title>How To Get Invited To speakerconf</title><content type="html">Every year it seems that someone is disappointed (or even insulted) that they didn't receive an invite to &lt;a href="http://speakerconf.com"&gt;speakerconf&lt;/a&gt;. If you haven't gotten an invite at this point, contrary to popular belief, it's not because Josh and I hate you. There's actually very little magic that goes into receiving a speakerconf invite, so it probably makes sense to put the formula out there.&lt;br&gt;
&lt;br&gt;
I'm not a big fan of receiving spam, and while I consider speakerconf to be a great event, I'm sure some people will consider speakerconf related email to be spam. With that in mind, I generally don't like to email people I don't know out of the blue. Along the same lines, some people are going to politely decline every invite I send their way. The less I know you, the less likely I am to continue inviting you, simply because I don't want to bother you. If you say to me "please keep me in mind in the future," I will. If you don't, I'll likely default on the safe side of not bothering you until you show some interest.&lt;br&gt;
&lt;br&gt;
So, if you haven't gotten an invite lately (or ever) it's likely only because I don't want to bother you.&lt;br&gt;
&lt;br&gt;
With that out of the way, here's roughly how we select who to invite to speakerconf:&lt;br&gt;
speakerconf needs sponsors to run in the way that Josh and I want to run it. If you've previously attended a speakerconf you can guarantee yourself an invite if your company is willing to become a sponsor. If you've never attended a speakerconf you can also guarantee yourself an invite by getting your company to become a sponsor and getting an endorsement from any one of the speakerconf alumni (list available at &lt;a href="http://speakerconf.com/speakers"&gt;http://speakerconf.com/speakers&lt;/a&gt;). If you are interested in discussing specifics, drop me an email (jay@jayfields.com).&lt;br&gt;
&lt;br&gt;
If you're not interested in sponsoring, your invite depends on space or endorsements. If you've previously attended a speakerconf and you'd like to return, all you need to do is let me know. If we have space at the next event, I'll gladly extend you an invite. If we don't have space, I'll extend you an invite to the following event.&lt;br&gt;
&lt;br&gt;
Future speakerconf events will likely have a maximum number of 20 attendees. Approximately 15 spots will be reserved for alumni, and the remaining ~5 spots will be available to anyone who has never attended a speakerconf. If you would like to grab one of those spots, all you need to do is get as many alumni as you can to email me and endorse you. They don't need to send a long email, something as simple as "you should invite Josh Graham to the next speakerconf" will do. Then, approximately 6 months before an upcoming speakerconf I will invite the 5 most alumni endorsed new comers.&lt;br&gt;
&lt;br&gt;
That's it. If you want to join us all you need to do is to utilize your network or become a sponsor. Hopefully this view into the invitation process will help avoid any disappointment in the future.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-3166506696534781620?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/HYXa8yNwg6w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/3166506696534781620/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=3166506696534781620" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3166506696534781620?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/3166506696534781620?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/HYXa8yNwg6w/how-to-get-invited-to-speakerconf.html" title="How To Get Invited To speakerconf" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/02/how-to-get-invited-to-speakerconf.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMBSHg7fyp7ImA9WhRUFEk.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-412329243819612051</id><published>2012-01-24T16:27:00.000-05:00</published><updated>2012-01-24T16:27:39.607-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-24T16:27:39.607-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="language adoption" /><category scheme="http://www.blogger.com/atom/ns#" term="languages" /><title>Lessons Learned while Introducing a New Programming Language</title><content type="html">I've used a lot of languages (professionally) over the years: (off the top of my head) Cold Fusion, HTML, Javascript, php, SQL, CSS, ASP(classic &amp; .net), C#, Ruby, Flex, Java, &amp; Clojure. Each language has pros and cons. Being a programmer, it's easiest to discuss the cons - and in general I believe it was best said:&lt;blockquote&gt;I hate all programming languages - Matt Foemmel&lt;/blockquote&gt;I think it's important to start with this in mind. At some point you're going to hate what you're advocating, so imagine how other people feel about it.&lt;br&gt;
&lt;br&gt;
In 2008 I introduced &lt;a href="http://clojure.org"&gt;Clojure&lt;/a&gt; into a &lt;a href="http://drw.com"&gt;DRW&lt;/a&gt; codebase. This blog entry focuses  on the adoption lessons I've learned in the past few years.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Language Selection&lt;/b&gt;&lt;br&gt;
Introducing a new language to an organization isn't an easy task; if you're going to succeed you're probably going to need to pick a language that satisfies a large technical need and is also considered socially acceptable. I was writing Java 100% of the time when I joined DRW, despite the fact that a large portion of the code I was writing only needed to run in eye-ball time (250ms). Java was absolutely the right choice for the Faster Than Eye-Ball Time code we were writing, but using Java for the other code always made me feel like I was unnecessarily paying the Java verbosity tax.&lt;br&gt;
&lt;br&gt;
On occasion I would mention the Java verbosity tax, and I found that my boss was actually interested in looking more closely at JRuby. I think JRuby would have been a win for us, but for me it was more interesting to hear that I had an ally if I wanted to explore non-Java options. If JRuby was on the table, then I knew that any high-level, dynamically typed language would also likely be on the table.&lt;br&gt;
&lt;br&gt;
However, before I'd even discovered any JRuby curiosity, I'd already begun looking into Haskell. Generally, software in trading firms needs to run "fast." If I was going to successfully introduce a new language it was going to need to run "almost as fast as Java." I'd heard good things about Haskell's speed, and it also fulfilled another adoption desire of mine:&lt;blockquote&gt;A language that doesn't affect the way you think about programming, is not worth knowing - Alan Perlis&lt;/blockquote&gt;I believed that if I found a language that was "performant enough", allowed us to deliver at a faster pace, and improved our programming skills then the level of effort required for adoption would be justified.&lt;br&gt;
&lt;br&gt;
I toyed with Haskell for a bit, but the adoption path seemed a bit too steep. There's the effort it takes to learn Haskell, but more importantly: we were already on the JVM. If I was going to get any support, I needed something that was easy to sneak into our existing infrastructure. Enter Clojure. Clojure is performant enough, more succinct than Java, and sufficiently different to anything else I'd previously worked with. Clojure is also dynamically typed and high level (like Ruby), so I was hoping to get some support from my boss.&lt;br&gt;
&lt;br&gt;
Causing my teammates as little pain as possible was a very large requirement - I believed this was key to gaining adoption. Clojure appeared to be the best choice due to the fact that we were already using:&lt;ul&gt;
&lt;li&gt;IntelliJ all day
&lt;li&gt;JUnit to run all of our tests
&lt;li&gt;TeamCity for CI &amp; artifact creation
&lt;li&gt;The JVM on our servers
&lt;li&gt;Yourkit for profiling&lt;/ul&gt;
&lt;br&gt;
Clojure was the language that gave me everything I was looking for and had the easiest adoption path for the rest of the team.&lt;br&gt;
&lt;br&gt;
I would have preferred Haskell or OCaml from a learning perspective, but they didn't seem like practical choices - and I doubt
I would have had as much success getting them in production. While I need to be an expert in many things Clojure related, I relied on the JVM server settings that someone else has determined to be "the best". If I had chosen Haskell or OCaml I'd have had to become an expert on a much larger scope of topics (e.g. deployment, memory model, libraries, new tools, etc).&lt;br&gt;
&lt;br&gt;
I believed then, and I still believe today that Clojure was the best choice given our technical needs and the social context.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Hello World&lt;/b&gt;&lt;br&gt;
Introducing a language is a delicate act. There's a bunch of valid concerns that you're going to need to address. I wasn't exactly sure how my teammates were going to react to the introduction of Clojure, so I wrote the original code in my own time at home. I knew that we needed some integration tests for our application; however, no one was actively working on implementing anything. I began by writing them in Java and then wrote Clojure versions as well. I knew enough Clojure that I was able to showcase it's succinctness - something I knew the team would value in integration tests. Additionally, since the tests aren't part of the production running code there were no real speed concerns to consider.&lt;br&gt;
&lt;br&gt;
Integration tests are a good place to introduce a new language, but any non-production code will probably be an equally good choice. For example, you could also choose database migration scripts, log file parsers, 3rd party software simulators, or deployment software. As long as you pick something that can fail without too much immediate pain you should be able to easily recover from any adoption issues.&lt;br&gt;
&lt;br&gt;
After I had both sets of tests complete I showed both versions to the other developers on the team. I pointed out why I preferred the Clojure versions and asked if they would be willing to give Clojure a chance. I also made commitments that made it hard for them to say no to the experiment.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Your Commitment&lt;/b&gt;&lt;br&gt;
I eased my teammates adoption fears by making the following commitments.&lt;ul&gt;
&lt;li&gt;If you want to work on the code I'll work with you (if you want me to work with you).
&lt;li&gt;If you don't want to work on the code I'll fix anything that's broken.
&lt;li&gt;If the initial pain of working with a new language becomes unbearable to you, I'll rewrite everything in Java on my own time.&lt;/ul&gt;Obviously you'll need to get team buy-in before writing too much in your new language - otherwise you could be setting yourself up for working a lot of nights and weekends.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Tool Support&lt;/b&gt;&lt;br&gt;
Chances are your team already has a tool-chain that they are happy with. Whatever that tool-chain is, your new language is going to need to play well within it. For me this meant being able to execute Clojure code within IntelliJ as easily as Java. For the most part the La Clojure plugin does the heavy lifting; however, I did need to write a testing framework that allowed me to run focused tests and seamlessly integrated with our existing JUnit test suite. The main point here is to remove any adoption friction that your team may be feeling. Learning a new language is a reasonable request, but changing the way a team works simply to accommodate an unproven (on that team) language choice is probably too much to ask.&lt;br&gt;
&lt;br&gt;
You may also need to make some sacrifices as well. I prefer to write Clojure in emacs; however, I'd rather be writing Clojure(where appropriate) in IntelliJ than writing Java in IntelliJ. During the early/fragile adoption time, you're the one who's going to need to do the majority of the compromising.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Find allies&lt;/b&gt;&lt;br&gt;
Chances are you'll have varying levels of interest in your new language. During the early days you should do anything you can to encourage others when they're interested; however, you don't want to push anything on anyone - that's the easiest way to find enemies. Hopefully you'll have a few teammates who are also as excited about a new language as you are - work closely with them on improving both of your skills. You'll want to get as many advocates as you can, otherwise you'll end up looking like the lone team member forcing the team to do something they aren't comfortable with.&lt;br&gt;
&lt;br&gt;
It's also inevitable that you'll end up needing more research time, needing tool support, and dealing with more production issues than you originally anticipated. You're going to need a few other people to pick up some of the slack when you find yourself overextended. Even when things are going well you'll find yourself in need of allies to help you support the growing code written in a new language.&lt;br&gt;
&lt;br&gt;
Lastly, the worst case scenario is you leaving a team and no one is left on the team that wants to support that code. It's pretty easy to see how a staffing situation can be portrayed as an adoption problem.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Know Everything&lt;/b&gt;&lt;br&gt;
Obviously you can't actually know everything, but you're going to need to have an answer or be able to quickly get an answer to anything that comes up. You'll definitely want to read a few books on your new language before putting it in any codebase that your team members also work with or rely on. That's likely not enough though, you'll also need to know where to go if you run into issues. For Clojure this was the IRC channel for immediate answers and the mailing list for less time sensitive issues or issues that required a bit more explanation. If you're really looking to cover your bases you'll want to have some type of relationship with the creator or one of the community leaders.&lt;br&gt;
&lt;br&gt;
Once people start adopting the language you introduced they're going to start doing things you didn't anticipate. You're going to need to know the dark corners of the language and the associated corner cases that exist. You'll also need to be an expert on issues such as memory allocation, performance, deployment, tool integration, library support, upgrade schedules, and everything else that is outside of the language's syntax.&lt;br&gt;
&lt;br&gt;
The more allies you have, the less you'll need to 'know everything'; however, at the end of the day you're going to need to know as much as possible. If things ever go wrong, all eyes are going to be on you. That's the level of commitment you're making by introducing a language, so you better know what you're getting into.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Get Help&lt;/b&gt;&lt;br&gt;
If your company is willing to let you introduce languages then they are probably a fairly supportive organization. Hopefully you'll have a bit of a training budget. See what opportunities you have for bringing in the language creator or the community leaders to work with you or provide training. If you're having issues with anything, having the creator of a language work with you is obviously a huge advantage. However, if things are going well it will probably benefit you to give up your training budget to contribute to a pool that could cover some training for other team mates who are interested in learning from the language's creator (or a community leader). Whether it's for you or interested allies, utilize your companies training budget to encourage adoption.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;Be an Advocate, not a Zealot&lt;/b&gt;&lt;br&gt;
At the end of the day it's not likely that everyone is going to have a &lt;a href="http://blog.jayfields.com/2011/01/compatible-opinions-on-software.html"&gt;compatible opinion&lt;/a&gt;. That's fine. Don't push your opinions on people who aren't interested. On most occasions the 'right' choice is the one that someone is passionate about. You might be passionate about your language, but a teammate of yours might be passionate about the old language. Neither of you needs to be right or wrong. People should work with what they are passionate about, and any attempt to make them work in another way is likely to do more harm than good. People who want to work with the new language will find a way to organize themselves together and people who don't will do the same thing. There's no reason to force adoption.&lt;br&gt;
&lt;br&gt;
Originally, I approached the problem as "If I create a clearly superior solution then everyone will want to come along." This definitely turned out not to be the case, thus the blog entry on &lt;a href="http://blog.jayfields.com/2011/01/compatible-opinions-on-software.html"&gt;compatible opinions on software&lt;/a&gt;. I learned that one person's "obviously better" is another person's "obviously worse." In the end the team ended up organically dividing into people who worked on the Clojure code and those that didn't. This worked out well for both parties, as the people who wanted to work with Clojure had enough available to them, and the people who didn't weren't forced to.&lt;br&gt;
&lt;br&gt;
The divide was more of a practice than an official split. We were all still part of "one team"; however, we tended to work on separate applications that communicated through messaging or not at all. I advocated strongly for a team split based on incompatible opinions and size (we were at 7, and I thought 4 and 3 would be fine), but we never ended up making anything official. Eventually other factors changed and the team shrunk to a size where a split was no longer necessary. I still believe splitting would have been the best solution if the team hadn't reorganized for other reasons.&lt;br&gt;
&lt;br&gt;
&lt;b&gt;The End&lt;/b&gt;&lt;br&gt;
Introducing a new language is likely a multi-year affair for any moderately sized organization. There's not likely an "end" where your responsibilities go back to what they were before introducing a new language. On the flip-side, you get to use what you consider to be the best tool for the job. Hopefully it's worth it when it's all said and done. Personally, I'm happy with my choice, but I expect to be learning new lessons on this topic for at least the next few years as well.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-412329243819612051?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/jc7w_3oHaiU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/412329243819612051/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=412329243819612051" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/412329243819612051?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/412329243819612051?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/jc7w_3oHaiU/lessons-learned-while-introducing-new.html" title="Lessons Learned while Introducing a New Programming Language" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>8</thr:total><feedburner:origLink>http://blog.jayfields.com/2012/01/lessons-learned-while-introducing-new.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0cERXY8fCp7ImA9WhRWEU8.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-5046509056728921785</id><published>2011-12-28T21:15:00.001-05:00</published><updated>2011-12-28T21:16:44.874-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-28T21:16:44.874-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="java interop" /><title>Convert java.util.Properties to a Clojure Map</title><content type="html">As I previously mentioned, a lot of the work I do involves &lt;a href="http://blog.jayfields.com/2011/12/clojure-java-interop.html"&gt;Clojure &amp; Java interop&lt;/a&gt;. This work includes the occasional case of working with a java.util.Properties object from within Clojure. Working with a Properties object isn't a huge deal, but while in Clojure I prefer to use &lt;a href="http://blog.jayfields.com/2010/07/clojure-destructuring.html"&gt;destructuring&lt;/a&gt; and the various functions (e.g. update-in, assoc, dissoc, etc) that are designed to work with Clojure maps.&lt;br&gt;&lt;br&gt;

The following example shows how easy it is to convert a Properties object to a Clojure map.&lt;pre&gt;user=&gt; (def prop-obj (doto (java.util.Properties.) (.putAll {"a" 1 "b" 2})))
user=&gt; prop-obj
#&amp;lt;Properties {b=2, a=1}&amp;gt;

user=&gt; (reduce (fn [x [y z]] (assoc x y z)) {} prop-obj)
{"a" 1, "b" 2}&lt;/pre&gt;That's fairly easy, but you'll quickly want your keys to be keywords if you plan on destructuring. You can drop in a quick call to keyword to convert the keys; however, you'll probably also want to dasherize the keys to allow for easy destructuring using keys and idiomatic names.&lt;pre&gt;user=&gt; (defn dash-match [[ _ g1 g2]]      
          (str g1 "-" g2))

user=&gt; (defn dasherize [k]
          (-&gt; k
            (clojure.string/replace #"([A-Z]+)([A-Z][a-z])" dash-match)
            (clojure.string/replace #"([a-z\d])([A-Z])" dash-match)
            (clojure.string/lower-case)))#'user/dash-match

user=&gt; (def prop-obj (doto (java.util.Properties.) (.putAll {"FirstName" "Mike" "LastName" "Green"})))
user=&gt; (reduce (fn [x [y z]] (assoc x y z)) {} prop-obj)                                              
{"LastName" "Green", "FirstName" "Mike"}
user=&gt; (reduce (fn [x [y z]] (assoc x (-&gt; y dasherize keyword) z)) {} prop-obj)
{:last-name "Green", :first-name "Mike"}&lt;/pre&gt;That looks good, but you might also find yourself working with a properties file that uses dots or dashes to group similar data. For example, you might find the following entry in your properties file (and the resulting Properties object).&lt;pre&gt;person.name=Mike Green
person.age=26
person.sex=male&lt;/pre&gt;Loading this into a map works fine; however, it would be nice if the resulting map was nested (to keep common data together, and for easier access using get-in, update-in, etc).&lt;br&gt;&lt;br&gt;The following code splits on dots and nests the values appropriately.&lt;pre&gt;user=&gt; (def prop-obj (doto (java.util.Properties.) (.putAll {"person.name" "Mike Green" "person.age" "26" "person.sex" "male"})))        
user=&gt; prop-obj                                                                                                                  
#&amp;lt;Properties {person.name=Mike Green, person.age=26, person.sex=male}&amp;gt;
user=&gt; (reduce (fn [x [y z]] (assoc-in x (-&gt; y dasherize (clojure.string/split #"\.")) z)) {} prop-obj)
{"person" {"sex" "male", "age" "26", "name" "Mike Green"}}

user=&gt; (-&gt; (reduce (fn [x [y z]] (assoc-in x (-&gt; y dasherize (clojure.string/split #"\.")) z)) {} prop-obj) clojure.walk/keywordize-keys)
{:person {:sex "male", :age "26", :name "Mike Green"}}&lt;/pre&gt;So, not too complicated, but much more complicated than what we started with. It turns out you'll likely want to do other things like parse integers, parse booleans, require keys, and add defaults when a key=value pair isn't specified.&lt;br&gt;&lt;br&gt;
We could go through the effort here of providing all that functionality, but instead I've created a small library that provides all of the above mentioned features: &lt;a href="https://github.com/jaycfields/propertea"&gt;propertea&lt;/a&gt;&lt;br&gt;&lt;br&gt;

If you'd like to see the implementation of the above features, you'll just need to look in propertea.core. If you want examples of all of the features of propertea, checkout the tests in github.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-5046509056728921785?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/wZ3wibZFikA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/5046509056728921785/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=5046509056728921785" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/5046509056728921785?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/5046509056728921785?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/wZ3wibZFikA/convert-javautilproperties-to-clojure.html" title="Convert java.util.Properties to a Clojure Map" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>1</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/12/convert-javautilproperties-to-clojure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEACRHk-eip7ImA9WhRWEEo.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-6277355033913160132</id><published>2011-12-28T06:46:00.000-05:00</published><updated>2011-12-28T06:46:05.752-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-28T06:46:05.752-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure functions" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="java interop" /><category scheme="http://www.blogger.com/atom/ns#" term="functions" /><title>Clojure &amp; Java Interop</title><content type="html">About a year ago I got a phone call asking if I wanted to join another team at &lt;a href="http://drw.com"&gt;DRW&lt;/a&gt;. The team supports a (primarily) Java application, but the performance requirements would also allow it to be written in a higher level language. I'd been writing Clojure (basically) full-time at that point - so my response was simple: I'd love to join, but I'm going to want to do future development using Clojure.&lt;br&gt;&lt;br&gt;

A year later we still have plenty of Java, but the vast majority of the new code I add is Clojure. One of the big reasons I'm able to use Clojure so freely is the seamless interop with Java.&lt;br&gt;&lt;br&gt;

&lt;b&gt;Execute Clojure from Java&lt;/b&gt;&lt;br&gt;
Calling Clojure from Java is as simple as loading the .clj file and invoking a method from that file. I used the &lt;a href="http://blog.jayfields.com/2009/05/calling-clojure-from-java.html"&gt;same example&lt;/a&gt; years ago, but I'll inline it here for simplicity.
&lt;pre&gt;; interop/core.clj
(ns interop.core)

(defn print-string [arg]
  (println arg))

// Java calling code
RT.loadResourceScript("interop/core.clj");
RT.var("interop.core", "print-string").invoke("hello world");&lt;/pre&gt;note: examples from this blog entry are available in &lt;a href="https://github.com/jaycfields/interop"&gt;this&lt;/a&gt; git repo. The commit with the code from the previous example is available &lt;a href="https://github.com/jaycfields/interop/commit/a6e0e472467707d0f6caa23fb48fb0beda130053"&gt;here&lt;/a&gt; and I'm running the example from the command line with: &lt;pre&gt;lein jar &amp;&amp; java -cp "interop-1.0.0.jar:lib/*" interop.Example&lt;/pre&gt;

&lt;b&gt;Execute Java from Clojure&lt;/b&gt;&lt;br&gt;At this point we have Java executing some Clojure code, and we also have Clojure using an object that was created in Java. Even though we're in Clojure we can easily call methods on any Java object.

&lt;pre&gt;(ns interop.core)

(defn print-string [arg]
  (println arg "is" (.length arg) "characters long"))&lt;/pre&gt;
&lt;a href="https://github.com/jaycfields/interop/commit/d771ecd52910933b2905a4df549c0a77b24c87a9"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;

The above code (using the length method of a String instance) produces the following output.
&lt;pre&gt;hello world is 11 characters long&lt;/pre&gt;

Calling a Java method and passing in additional arguments is also easy in Clojure.&lt;pre&gt;(ns interop.core)

(defn print-string [arg]
  (println (.replace arg "hello" "goodbye")))&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/a6260d33fbd8a6d47368cc393975e1958c493418"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;The above code produces the following output.&lt;pre&gt;goodbye world&lt;/pre&gt;

There are a few other things to know about calling Java from Clojure. The following examples show how to call static methods, use enums, and use inner classes.&lt;pre&gt;(ns interop.core)

(defn print-string [arg]
  ;;; calling a static method
  (println (String/valueOf true))

  ;;; using an enum
  (println (java.util.concurrent.TimeUnit/SECONDS))

  ;;; using a Java nested (inner) class. Note, in Clojure you
  ;;; use a $ instead of a .
  (println (java.util.AbstractMap$SimpleEntry. "key" "val")))
&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/2a56ba343c7365d48478b0281e7d3694011105f5"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;And, the output:&lt;pre&gt;true
#&amp;lt; SECONDS&amp;gt;
#&amp;lt;SimpleEntry key=val&amp;gt;&lt;/pre&gt;

&lt;b&gt;Create Java objects in Clojure&lt;/b&gt;&lt;br&gt;
When working with Clojure you'll likely want to interact with existing Java objects, but you'll probably also want to create new instances of Java objects. You might have noticed the dot at the end of Abstract$SimpleEntry. in the previous example - that's how you instruct Clojure to create an instance of a Java object. The following example shows the dot notation for calling a constructor of the String class.&lt;pre&gt;(ns interop.core)

(defn print-string [arg]
  (println (String. arg)))&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/d762764aa7c66224c2f9636f56f3c0ab709bb43d"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;At this point our output is back to the original output.&lt;pre&gt;hello world&lt;/pre&gt;When creating Java objects it's often beneficial to know which Java interfaces the Clojure data structures implement. The following examples demonstrate how you can create Java objects while passing Clojure datastructures (and functions) as constructor arguments.
&lt;pre&gt;(ns interop.core)

(defn print-string [arg]
  ;;; pass a Clojure vector where Java expects a java.util.Collection
  (println (java.util.HashSet. ["1" "2"]))

  ;;; pass a Clojure map where Java expects a java.util.Map
  (println (java.util.LinkedHashMap. {1 "1" 2 "2"}))

  ;;; pass a Clojure function where Java expects a Runnable
  (println (Thread. (fn [] (println "clojure fns are runnables (and callables)")))))&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/ea5a526222da3046c6134bd3a4fb7d62392479f7"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;

The output shows the constructed Java objects.&lt;pre&gt;#&amp;lt;HashSet [2, 1]&amp;gt;
#&amp;lt;LinkedHashMap {1=1, 2=2}&amp;gt;
#&amp;lt;Thread Thread[Thread-1,5,main]&amp;gt;&lt;/pre&gt;Calling constructors in Clojure is very easy, but that's not always an option when creating a Java object. At times you will likely need to create an instance of a Java interface. Clojure provides both &lt;a href="http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/proxy"&gt;proxy&lt;/a&gt; and &lt;a href="http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/reify"&gt;reify&lt;/a&gt; for creating instances of Java interfaces. The following example demonstrates the syntax for using either proxy or reify.&lt;pre&gt;(ns interop.core)

(defn proxy-coll []
  (proxy [java.util.Collection] []
    (add [o]
         (println o)
         true)))

(defn reify-coll []
  (reify java.util.Collection
    (add [this o]
         (println o)
         (println this)
         true)))

(defn main []
  (.add (proxy-coll) "this string is printed on proxied.add")
  (.add (reify-coll) "this string is printed on reified.add"))&lt;/pre&gt;

&lt;a href="https://github.com/jaycfields/interop/commit/783eb6f61cb37bf617e645e1ecea7156bc221754"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;

note, I also changed Example.java (the details are available in the above linked commit).

The syntax for proxy and reify are fairly similar, and both offer additional options that are worth looking into. The primary differences between these two simple examples are:&lt;ul&gt;&lt;li&gt;The proxy implementation requires an empty vector where we could specify constructor arguments (if this were an abstract class instead of an interface).&lt;li&gt;The arg list for all methods of reify will specify the reified instance as the first argument. In our example the Collection.add method only takes one argument, but in our reify we also get the instance of the collection.&lt;/ul&gt; You might have also noticed that both implementations of add have "true" at the end - in our example we're hard-coding the return value of add to always return true.

The following output is the result of running the current example code.&lt;pre&gt;this string is printed on proxied.add
this string is printed on reified.add
#&amp;lt;core$reify_coll$reify__11 interop.core$reify_coll$reify__11@556917ee&amp;gt;&lt;/pre&gt;

It's worth reading the docs to determine whether you want proxy or reify; however, if you don't see a clear choice I would opt for reify.&lt;br&gt;&lt;br&gt;

&lt;b&gt;Returning objects from Clojure to Java&lt;/b&gt;&lt;br&gt;
Our current Example.java returns something from the call to invoke on the clojure.lang.Var that is returned from &lt;code&gt;RT.var("interop.core", "main")&lt;/code&gt;, but we're ignoring it so we have no idea what's returned.* Let's change the code and return something on purpose.&lt;pre&gt;// interop/Example.java
package interop;

import clojure.lang.RT;

public class Example {
    public static void main(String[] args) throws Exception {
        RT.loadResourceScript("interop/core.clj");
        System.out.println(RT.var("interop.core", "main").invoke());
    }
}

; interop/core.clj
(ns interop.core)

(defn main []
  {:a "1" :b "2"})&lt;/pre&gt;Running our changes produces the following output.&lt;pre&gt;{:a "1", :b "2"}&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/67131ff1a34c4278db73e80b3ca5b17554422e76"&gt;commit&lt;/a&gt;

&lt;br&gt;&lt;br&gt;
At this point we are back in Java land after making a quick trip to Clojure to get a value. Returning most objects will be pretty straightforward; however, at some point you may want to return a Clojure function. This turns out to be fairly easy as well, since Clojure functions are instances of the IFn interface. The following code demonstrates how to return a Clojure function and call it from within Java.&lt;pre&gt;// interop/Example.java
package interop;

import clojure.lang.RT;

public class Example {
    public static void main(String[] args) throws Exception {
        RT.loadResourceScript("interop/core.clj");
        clojure.lang.IFn f = (clojure.lang.IFn) RT.var("interop.core", "main").invoke();
        f.invoke("hello world");
    }
}

// interop/core.clj
(ns interop.core)

(defn main [] println)&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/f836fa1bdf007c2dae4e9480649757c78c4de29e"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;

The above example returns the println function from interop.core/main and then invokes the println function from within Java. I only chose to pass one argument to invoke; however, the IFn.invoke method has various overrides to allow you to pass several arguments.

The above code works, but it can be simplified to the following example.&lt;pre&gt;package interop;

import clojure.lang.RT;

public class Example {
    public static void main(String[] args) throws Exception {
        clojure.lang.IFn f = (clojure.lang.IFn) RT.var("clojure.core", "println");
        f.invoke("hello world");
    }
}&lt;/pre&gt;&lt;a href="https://github.com/jaycfields/interop/commit/6fd8343d5367b7f1a6b6841f141368b225f9f83f"&gt;commit&lt;/a&gt;&lt;br&gt;&lt;br&gt;

It seems like a fitting end that our final output is the same as our original output.&lt;pre&gt;hello world&lt;/pre&gt;

*actually, it's the last thing that's returned, or "true" for this specific case.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-6277355033913160132?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/m5-S5ZUK7J0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/6277355033913160132/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=6277355033913160132" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6277355033913160132?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/6277355033913160132?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/m5-S5ZUK7J0/clojure-java-interop.html" title="Clojure &amp; Java Interop" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>3</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/12/clojure-java-interop.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUYCQXc7fyp7ImA9WhRSF0w.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-2532370811353092916</id><published>2011-11-19T08:24:00.001-05:00</published><updated>2011-11-19T09:32:40.907-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-19T09:32:40.907-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="functional testing" /><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations - scenarios</title><content type="html">When I set out to write &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; I wanted to create a simple unit testing framework. I'm happy with what expectations provides for unit testing; however, I also need to write the occasional test that changes values or causes a side effect. There's no way I could go back to clojure.test after enjoying better failure messages, trimmed stack traces, automatic testing running, etc. Thus, expectations.scenarios was born.&lt;br&gt;&lt;br&gt;

Using expectations.scenarios should be fairly natural if you already use expectations. The following example is a simple scenario (which could be a unit test, but we'll start here for simplicity).&lt;pre&gt;(ns example.scenarios
  (:use expectations.scenarios))

(scenario
 (expect nil? nil))&lt;/pre&gt;A quick trip to the command line shows us that everything is working as expected.&lt;pre&gt;Ran 1 tests containing 1 assertions in 4 msecs
0 failures, 0 errors.&lt;/pre&gt;As I said above, you could write this test as a unit test. However, expectations.scenarios was created for the cases in which you want to verify a value, make a change, and verify a value again. The following example shows multiple expectations verifying changing values in the same scenario.&lt;pre&gt;(scenario
  (let [a (atom 0)]
    (swap! a inc)
    (expect 1 @a)
    (swap! a inc)
    (expect 2 @a)))&lt;/pre&gt;In expectations (unit tests) you can only have one expect (or given) so failures are captured, but they do not stop execution. However, due to the procedural nature of scenarios, the first failing expect stops execution.&lt;pre&gt;(scenario
  (let [a (atom 0)]
    (swap! a inc)
    (expect 2 @a)
    (println "you'll never see this")))

failure in (scenarios.clj:4) : example.scenarios
           (expect 2 (clojure.core/deref a))
           expected: 2 
                was: 1
           on (scenarios.clj:7)
Ran 1 tests containing 1 assertions in 81 msecs
1 failures, 0 errors.&lt;/pre&gt;expectations.scenarios also allows you to easily verify calls to any function. I generally use interaction expects when I need to verify some type of side effect (e.g. logging or message publishing).&lt;pre&gt;(scenario
 (println "1")
 (expect (interaction (println "1"))))&lt;/pre&gt;It's important to note the ordering of this scenario. You don't 'setup' an expectation and then call the function. Exactly the opposite is true - you call the function the same way you would in production, then you expect the interaction to have occurred. You may find this jarring if you're used to setting up your mocks ahead of time; However, I think this syntax is the least intrusive - and I think you'll prefer it in the long term.&lt;br&gt;&lt;br&gt;The above example calls println directly, but your tests are much more likely to look something like this.&lt;pre&gt;(defn foo [x] (println x))

(scenario
 (foo "1")
 (expect (interaction (println "1"))))&lt;/pre&gt;Similar to all other mocking frameworks (that I know of) the expect is using an implicit "once" argument. You can also specify :twice and :never if you find yourself needing those interaction tests.&lt;pre&gt;(defn foo [x] (println x))

(scenario
 (foo "1")
 (foo "1")
 (expect (interaction (println "1")) :twice)
 (expect (interaction (identity 1)) :never))&lt;/pre&gt;On occasion you may find yourself interested in verifying 2 out of 3 arguments - expectations.scenarios provides the 'anything' var that can be used for arguments you don't care about.&lt;pre&gt;(defn foo [x y z] (println x y z))

(scenario
 (foo "1" 2 :a)
 (expect (interaction (println "1" anything :a))))&lt;/pre&gt;That's about all there is to expectations.scenarios, hopefully it fills the gap for tests you want to write that simply can't be done as unit tests.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-2532370811353092916?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/H-qO2ky4-Y8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/2532370811353092916/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=2532370811353092916" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/2532370811353092916?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/2532370811353092916?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/H-qO2ky4-Y8/clojure-expectations-scenarios.html" title="Clojure: expectations - scenarios" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-scenarios.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QFQnszfSp7ImA9WhRTEUg.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-8374573026753956504</id><published>2011-11-01T06:59:00.003-04:00</published><updated>2011-11-01T08:41:53.585-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T08:41:53.585-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations unit testing wrap-up</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six (this entry)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The previous blog posts on &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; unit testing syntax cover all of the various ways that expectations can be used to write tests and what you can expect when your tests fail. However, there are a few other things worth knowing about expectations.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Stacktraces&lt;/span&gt;&lt;br /&gt;expectations aggressively removes lines from the stacktraces. Just like many other aspects of expectations, the focus is on more signal and less noise. Any line in the stacktrace from clojure.core, clojure.lang, clojure.main, and java.lang will be removed. As a result any line appearing in your stacktrace should be relevant to your application or a third-party lib you're using. expectations also removes any duplicates that can occasionally appear when anonymous functions are part of the stacktrace. Again, it's all about improving signal by removing noise. Speaking of noise...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Test Names&lt;/span&gt;&lt;br /&gt;You might have noticed that expectations does not require you to create a test name. This is a reflection of my personal opinion that &lt;a href="http://blog.jayfields.com/2008/05/testing-value-of-test-names.html"&gt;test names are nothing more than comments&lt;/a&gt; and shouldn't be required. If you desire test names, feel free to drop a comment above each test. Truthfully, this is probably a better solution anyway, since you can use spaces (instead of dashes) to separate words in a comment. Comments are good when used properly, but they can become noise when they are required. The decision to simply use comments for test names is another example of improving signal by removing noise.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Running Focused Expectations&lt;/span&gt;&lt;br /&gt;Sometimes you'll have a file full of expectations, but you only want to run a specific expectation - expectations solves this problem by giving you 'expect-focused'. If you use expect-focused only expectations that are defined using expect-focused will be run.&lt;br /&gt;&lt;br /&gt;For example, if you have the following expectations in a file you should see the following results from 'lein expectations'.&lt;pre&gt;(ns sample.test.core&lt;br /&gt;  (:use [expectations]))&lt;br /&gt;&lt;br /&gt;(expect zero? 0)&lt;br /&gt;(expect zero? 1)&lt;br /&gt;(expect-focused nil? nil)&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 1 tests containing 1 assertions in 2 msecs&lt;br /&gt;IGNORED 2 EXPECTATIONS&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;As you can see, expectations only ran one test - the expect-focused on line 6. If the other tests had been run the test on line 5 would have created a failure. It can be easy to accidentally leave a few expect-focused calls in, so expectations prints the number of ignored expectations in capital letters as a reminder. Focused expectation running is yet another way to remove noise while working through a problem.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Tests Running&lt;/span&gt;&lt;br /&gt;If you always use 'lein expectations' to run your tests you'll never even care; however, if you ever want to run individual test files it's important to know that your tests run by default on JVM shutdown. When I'm working with Clojure and Java I usually end up using IntelliJ, and therefore have the ability to easily run individual files. When I switched from clojure.test to expectations I wanted to make test running as simple as possible - so I removed the need to specify (run-all-tests). Of course, if you don't want expectations to run for some reason you can disable this feature by calling (expectations/disable-run-on-shutdown).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;JUnit Integration&lt;/span&gt;&lt;br /&gt;Lack of JUnit integration was a deal breaker for my team in the early days, so expectations comes with an easy way to run all tests as part of JUnit. If you want all of your tests to run in JUnit all you need to do is implement ExpectationsTestRunner.TestSource. The following example is what I use to run all the tests in expectations with JUnit.&lt;pre&gt;import expectations.junit.ExpectationsTestRunner;&lt;br /&gt;import org.junit.runner.RunWith;&lt;br /&gt;&lt;br /&gt;@RunWith(expectations.junit.ExpectationsTestRunner.class)&lt;br /&gt;public class SuccessTest implements ExpectationsTestRunner.TestSource{&lt;br /&gt;&lt;br /&gt;    public String testPath() {&lt;br /&gt;        return "test/clojure/success";&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;As you can see from the example above, all you need to do is tell the test runner where to find your Clojure files.&lt;br /&gt;&lt;br /&gt;That should be everything you need to know about expectations for unit testing use. If anything is unclear, please drop me a line in the comments.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-8374573026753956504?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/TDY8q4DXI8g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/8374573026753956504/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=8374573026753956504" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/8374573026753956504?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/8374573026753956504?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/TDY8q4DXI8g/clojure-expectations-unit-testing-wrap.html" title="Clojure: expectations unit testing wrap-up" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4BQHsyeSp7ImA9WhRTEUg.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-2918150970874007660</id><published>2011-11-01T06:58:00.003-04:00</published><updated>2011-11-01T08:35:51.591-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T08:35:51.591-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations - removing duplication with given</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five (this entry)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; obviously has a bias towards &lt;a href="http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html"&gt;one assertion per test&lt;/a&gt;; however, there are times that verifying several things at the same time does make sense. For example, if you want to verify a few different properties of the same Java object it probably makes sense to make multiple assertions on the same instance.&lt;br /&gt;&lt;br /&gt;One of the biggest problems with multiple assertions per test is when your test follows this pattern:&lt;ol&gt;&lt;li&gt;create some state&lt;li&gt;verify a bit about the state&lt;li&gt;alter the state&lt;li&gt;verify more about the state&lt;/ol&gt;Part of the problem is that the assertions that occurred before the altering of the state may or may not be relevant after the alteration. Additionally, if any of the assertions fail you have to stop running the entire test - thus some of your assertions will not be run (and you'll be lacking some information).&lt;br /&gt;&lt;br /&gt;expectations takes an alternate route - embracing the idea of multiple assertions by providing a specific syntax that allows multiple verifications and the least amount of duplication.&lt;br /&gt;&lt;br /&gt;The following example shows how you can test multiple properties of a Java object using the 'given' syntax.&lt;pre&gt;(given (java.util.ArrayList.)&lt;br /&gt;       (expect&lt;br /&gt;         .size 0&lt;br /&gt;         .isEmpty true))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 2 tests containing 2 assertions in 4 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;The syntax is simple enough: (given an-object (expect method return-value [method return-value])) &lt;br /&gt;note: [method return-value] may be repeated any number of times.&lt;br /&gt;&lt;br /&gt;This syntax allows us to expect return-values from as many methods as we care to verify, but encourages us not to change any state between our various assertions. This syntax also allows us to to run each assertion regardless of the outcome of any previous assertion.&lt;br /&gt;&lt;br /&gt;Obviously you could call methods that change the internal state of the object and at that point you're on your own. I definitely wouldn't recommend testing that way. However, as long as you call methods that don't change any state 'given' can help you write succinct tests that verify as many aspects of an object as you need to test.&lt;br /&gt;&lt;br /&gt;As usual, I'll show the output for tests that fail using this syntax.&lt;pre&gt;(given (java.util.ArrayList.)&lt;br /&gt;       (expect&lt;br /&gt;         .size 1&lt;br /&gt;         .isEmpty false))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:4) : sample.test.core&lt;br /&gt;           (expect 1 (.size (java.util.ArrayList.)))&lt;br /&gt;           expected: 1 &lt;br /&gt;                was: 0&lt;br /&gt;failure in (core.clj:4) : sample.test.core&lt;br /&gt;           (expect false (.isEmpty (java.util.ArrayList.)))&lt;br /&gt;           expected: false &lt;br /&gt;                was: true&lt;/pre&gt;This specific syntax was created for testing Java objects, but an interesting side effect is that it actually works on any value and you can substitute method calls with any function. For example, you can test a vector or a map using the examples below as a template.&lt;pre&gt;(given [1 2 3]&lt;br /&gt;       (expect&lt;br /&gt;         first 1&lt;br /&gt;         last 3))&lt;br /&gt;&lt;br /&gt;(given {:a 2 :b 4}&lt;br /&gt;       (expect &lt;br /&gt;         :a 2&lt;br /&gt;         :b 4))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 4 tests containing 4 assertions in 8 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;And, of course, the failures.&lt;pre&gt;(given [1 2 3]&lt;br /&gt;       (expect&lt;br /&gt;         first 2&lt;br /&gt;         last 1))&lt;br /&gt;&lt;br /&gt;(given {:a 2 :b 4}&lt;br /&gt;       (expect &lt;br /&gt;         :a 1&lt;br /&gt;         :b 1))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:4) : sample.test.core&lt;br /&gt;           (expect 2 (first [1 2 3]))&lt;br /&gt;           expected: 2 &lt;br /&gt;                was: 1&lt;br /&gt;failure in (core.clj:4) : sample.test.core&lt;br /&gt;           (expect 1 (last [1 2 3]))&lt;br /&gt;           expected: 1 &lt;br /&gt;                was: 3&lt;br /&gt;failure in (core.clj:9) : sample.test.core&lt;br /&gt;           (expect 1 (:a {:a 2, :b 4}))&lt;br /&gt;           expected: 1 &lt;br /&gt;                was: 2&lt;br /&gt;failure in (core.clj:9) : sample.test.core&lt;br /&gt;           (expect 1 (:b {:a 2, :b 4}))&lt;br /&gt;           expected: 1 &lt;br /&gt;                was: 4&lt;br /&gt;Ran 4 tests containing 4 assertions in 14 msecs&lt;br /&gt;4 failures, 0 errors.&lt;/pre&gt;When you want to call methods on a Java object or call functions with the same instance over and over the previous given syntax is really the simplest solution. However, there are times where you want something more flexible.&lt;br /&gt;&lt;br /&gt;expectations also has a 'given' syntax that allows you to specify a template - thus reducing code duplication. The following example shows a test that verifies + with various arguments.&lt;pre&gt;(given [x y] (expect 10 (+ x y))&lt;br /&gt;       4 6&lt;br /&gt;       6 4&lt;br /&gt;       12 -2)&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 3 tests containing 3 assertions in 5 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;The syntax for this flavor of given is: (given bindings template-form values-to-be-bound). The template form can be anything you need - just remember to put the expect in there.&lt;br /&gt;&lt;br /&gt;Here's another example where we combine given with in to test a few different things. This example shows both the successful and failing versions.&lt;pre&gt;;; successful&lt;br /&gt;(given [x y] (expect x (in y))&lt;br /&gt;       :a #{:a :b}&lt;br /&gt;       {:a :b} {:a :b :c :d})&lt;br /&gt;&lt;br /&gt;;; failure&lt;br /&gt;(given [x y] (expect x (in y))&lt;br /&gt;       :c #{:a :b}&lt;br /&gt;       {:a :d} {:a :b :c :d})&lt;br /&gt;&lt;br /&gt;lein expectations&lt;br /&gt;failure in (core.clj:8) : sample.test.core&lt;br /&gt;           (expect :c (in #{:a :b}))&lt;br /&gt;           key :c not found in #{:a :b}&lt;br /&gt;failure in (core.clj:8) : sample.test.core&lt;br /&gt;           (expect {:a :d} (in {:a :b, :c :d}))&lt;br /&gt;           expected: {:a :d} &lt;br /&gt;                 in: {:a :b, :c :d}&lt;br /&gt;           :a expected: :d&lt;br /&gt;                   was: :b&lt;br /&gt;Ran 4 tests containing 4 assertions in 13 msecs&lt;br /&gt;2 failures, 0 errors.&lt;/pre&gt;That's basically it for 'given' syntax within expectations. There are times that I use all of the various versions of given; however, there seems to be a connection with using given and interacting with Java objects. If you don't find yourself using Java objects very often then you probably wont have a strong need for given.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-2918150970874007660?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/UZPJyoQ39CM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/2918150970874007660/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=2918150970874007660" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/2918150970874007660?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/2918150970874007660?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/UZPJyoQ39CM/clojure-expectations-removing.html" title="Clojure: expectations - removing duplication with given" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-removing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MBQ3w5cCp7ImA9WhRTEUk.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-400249871918621188</id><published>2011-11-01T06:57:00.002-04:00</published><updated>2011-11-01T07:04:12.228-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T07:04:12.228-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations and Double/NaN</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four (this entry)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm not really a fan of Double/NaN in general, but sometimes it seems like the least evil choice. When I find myself in one of those cases I always hate having to write tests in a way that differs from all the other tests in the codebase. A goal I've always had with expectations is to keep the syntax consistent, and as a result I've chosen to treat Double/NaN as equal to Double/NaN when in the various Clojure data structures.&lt;br /&gt;&lt;br /&gt;The following examples demonstrate Double/NaN being treated as equal&lt;pre&gt;&lt;br /&gt;;; allow Double/NaN equality in a map&lt;br /&gt;(expect {:a Double/NaN :b {:c Double/NaN}} {:a Double/NaN :b {:c Double/NaN}})&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality in a set&lt;br /&gt;(expect #{1 Double/NaN} #{1 Double/NaN})&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality in a list&lt;br /&gt;(expect [1 Double/NaN] [1 Double/NaN])&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 3 tests containing 3 assertions in 32 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;As you would expect, you can also count on Double/NaN being considered equal even if you are using the 'in' function.&lt;pre&gt;;; allow Double/NaN equality when verifying values are in a map&lt;br /&gt;(expect {:a Double/NaN :b {:c Double/NaN}} (in {:a Double/NaN :b {:c Double/NaN} :d "other stuff"}))&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality when verifying it is in a set&lt;br /&gt;(expect Double/NaN (in #{1 Double/NaN}))&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality when verifying it's existence in a list&lt;br /&gt;(expect Double/NaN (in [1 Double/NaN]))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 3 tests containing 3 assertions in 32 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;For completeness I'll also show the examples of each of these examples failing.&lt;pre&gt;;; allow Double/NaN equality in a map&lt;br /&gt;(expect {:a Double/NaN :b {:c Double/NaN}} {:a nil :b {:c Double/NaN}})&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality with in fn and map&lt;br /&gt;(expect {:a Double/NaN :b {:c nil}} (in {:a Double/NaN :b {:c Double/NaN} :d "other stuff"}))&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality in a set&lt;br /&gt;(expect #{1 Double/NaN} #{1 nil})&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality with in fn and set&lt;br /&gt;(expect Double/NaN (in #{1 nil}))&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality in a list&lt;br /&gt;(expect [1 Double/NaN] [1 nil])&lt;br /&gt;&lt;br /&gt;;; allow Double/NaN equality with in fn and list&lt;br /&gt;(expect Double/NaN (in [1 nil]))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:5) : sample.test.core&lt;br /&gt;           (expect {:a Double/NaN, :b {:c Double/NaN}}&lt;br /&gt;                   {:a nil, :b {:c Double/NaN}})&lt;br /&gt;           expected: {:a NaN, :b {:c NaN}} &lt;br /&gt;                was: {:a nil, :b {:c NaN}}&lt;br /&gt;           :a expected: NaN&lt;br /&gt;                   was: nil&lt;br /&gt;failure in (core.clj:8) : sample.test.core&lt;br /&gt;           (expect {:a Double/NaN, :b {:c nil}} (in {:a Double/NaN, :b {:c Double/NaN}, :d "other stuff"}))&lt;br /&gt;           expected: {:a NaN, :b {:c nil}} &lt;br /&gt;                 in: {:a NaN, :b {:c NaN}, :d "other stuff"}&lt;br /&gt;           :b {:c expected: nil&lt;br /&gt;                       was: NaN&lt;br /&gt;failure in (core.clj:11) : sample.test.core&lt;br /&gt;           (expect #{1 Double/NaN} #{nil 1})&lt;br /&gt;           expected: #{NaN 1} &lt;br /&gt;                was: #{nil 1}&lt;br /&gt;           nil are in actual, but not in expected&lt;br /&gt;           NaN are in expected, but not in actual&lt;br /&gt;failure in (core.clj:14) : sample.test.core&lt;br /&gt;           (expect Double/NaN (in #{nil 1}))&lt;br /&gt;           key NaN not found in #{nil 1}&lt;br /&gt;failure in (core.clj:17) : sample.test.core&lt;br /&gt;           (expect [1 Double/NaN] [1 nil])&lt;br /&gt;           expected: [1 NaN] &lt;br /&gt;                was: [1 nil]&lt;br /&gt;           nil are in actual, but not in expected&lt;br /&gt;           NaN are in expected, but not in actual&lt;br /&gt;failure in (core.clj:20) : sample.test.core&lt;br /&gt;           (expect Double/NaN (in [1 nil]))&lt;br /&gt;           value NaN not found in [1 nil]&lt;br /&gt;Ran 6 tests containing 6 assertions in 66 msecs&lt;br /&gt;6 failures, 0 errors.&lt;/pre&gt;There always seems to be downsides to using NaN, so I tend to look for the least painful path. Hopefully expectations provides the most pain-free path when your tests end up needing to include NaN.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-400249871918621188?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/F8xBhOnnC14" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/400249871918621188/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=400249871918621188" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/400249871918621188?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/400249871918621188?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/F8xBhOnnC14/clojure-expectations-and-doublenan.html" title="Clojure: expectations and Double/NaN" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MGQH07cCp7ImA9WhRTEUk.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-1168706987665039490</id><published>2011-11-01T06:56:00.002-04:00</published><updated>2011-11-01T07:03:41.308-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T07:03:41.308-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations with values in vectors, sets, and maps</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three (this entry)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've previously written about verifying equality and the various non-equality &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; that are available. This entry will focus on another type of comparison that is allowed in expectations - verifying that an 'expected' value is in an 'actual' value.&lt;br /&gt;&lt;br /&gt;A quick recap - expectations generally look like this: (expect expected actual)&lt;br /&gt;&lt;br /&gt;verifying an expected value is in an actual value is straightforward and hopefully not a surprising syntax: (expect expected (in actual))&lt;br /&gt;&lt;br /&gt;If that's not clear, these examples should make the concept completely clear.&lt;pre&gt;;; expect a k/v pair in a map.&lt;br /&gt;(expect {:foo 1} (in {:foo 1 :cat 4}))&lt;br /&gt;&lt;br /&gt;;; expect a key in a set&lt;br /&gt;(expect :foo (in #{:foo :bar}))&lt;br /&gt;&lt;br /&gt;;; expect a val in a list&lt;br /&gt;(expect :foo (in [:foo :bar]))&lt;/pre&gt;As you would expect, running these expectations results in 3 passing tests.&lt;pre&gt;jfields$ lein expectations&lt;br /&gt;Ran 3 tests containing 3 assertions in 8 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;As usual, I'll show the failures as well.&lt;pre&gt;;; expect a k/v pair in a map.&lt;br /&gt;(expect {:foo 2} (in {:foo 1 :cat 4}))&lt;br /&gt;&lt;br /&gt;;; expect a key in a set&lt;br /&gt;(expect :baz (in #{:foo :bar}))&lt;br /&gt;&lt;br /&gt;;; expect a val in a list&lt;br /&gt;(expect :baz (in [:foo :bar]))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:18) : sample.test.core&lt;br /&gt;           (expect {:foo 2} (in {:foo 1, :cat 4}))&lt;br /&gt;           expected: {:foo 2} &lt;br /&gt;                 in: {:foo 1, :cat 4}&lt;br /&gt;           :foo expected: 2&lt;br /&gt;                     was: 1&lt;br /&gt;failure in (core.clj:21) : sample.test.core&lt;br /&gt;           (expect :baz (in #{:foo :bar}))&lt;br /&gt;           key :baz not found in #{:foo :bar}&lt;br /&gt;failure in (core.clj:24) : sample.test.core&lt;br /&gt;           (expect :baz (in [:foo :bar]))&lt;br /&gt;           value :baz not found in [:foo :bar]&lt;/pre&gt;expectations does it's best to provide you with any additional info that might be helpful. In the case of the vector and the set there's not much else that can be said; however, the map failure gives you additional information that can be used to track down the issue.&lt;br /&gt;&lt;br /&gt;There's nothing magical going on with 'in' expectations and you could easily do the equivalent with select-keys, contains?, or some, but expectations allows you to get that behavior while keeping your tests succinct.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-1168706987665039490?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/BsF1rhoCC8w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/1168706987665039490/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=1168706987665039490" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/1168706987665039490?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/1168706987665039490?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/BsF1rhoCC8w/clojure-expectations-with-values-in.html" title="Clojure: expectations with values in vectors, sets, and maps" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIHQns5eSp7ImA9WhRTEUg.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-5329122265953925422</id><published>2011-11-01T06:55:00.004-04:00</published><updated>2011-11-01T08:28:53.521-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T08:28:53.521-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: Non-equality expectations</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two (this entry)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In my last blog post I gave examples of how to use &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt; to test for equality. This entry will focus on non-equality expectations that are also available.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Regex&lt;/span&gt;&lt;br /&gt;expectations allows you to specify that you expect a regex, and if the string matches that regex the expectation passes. The following example shows both the successful and failing expectations that use regexes.&lt;pre&gt;&lt;br /&gt;(expect #"in 14" "in 1400 and 92")&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 1 tests containing 1 assertions in 4 msecs&lt;br /&gt;0 failures, 0 errors.&lt;br /&gt;&lt;br /&gt;(expect #"in 14" "in 1300 and 92")&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:17) : sample.test.core&lt;br /&gt;           (expect in 14 in 1300 and 92)&lt;br /&gt;           regex #"in 14" not found in "in 1300 and 92"&lt;br /&gt;Ran 1 tests containing 1 assertions in 5 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;As you can see from the previous example, writing an expectation using a regex is syntactically the same as writing an equality expectation - and this is true for all of the non-equality expectations. In expectations there is only one syntax for expect - it's always (expect expected actual).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Testing for a certain type&lt;/span&gt;&lt;br /&gt;I basically never write tests that verify the result of a function is a certain type. However, for the once in a blue moon case where that's what I need, expectations allows me to verify that the result of a function call is a certain type simply by using that type as the expected value. The example below shows the successful and failing examples of testing that the actual is an instance of the expected type.&lt;pre&gt;(expect String "in 1300 and 92")&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 1 tests containing 1 assertions in 6 msecs&lt;br /&gt;0 failures, 0 errors.&lt;br /&gt;&lt;br /&gt;(expect Integer "in 1300 and 92")&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:17) : sample.test.core&lt;br /&gt;           (expect Integer in 1300 and 92)&lt;br /&gt;           in 1300 and 92 is not an instance of class java.lang.Integer&lt;br /&gt;Ran 1 tests containing 1 assertions in 5 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;&lt;span style="font-weight:bold;"&gt;Expected Exceptions&lt;/span&gt;&lt;br /&gt;Expected exceptions are another test that I rarely write; however, when I find myself in need - expectations has me covered.&lt;pre&gt;(expect ArithmeticException (/ 12 0))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;Ran 1 tests containing 1 assertions in 6 msecs&lt;br /&gt;0 failures, 0 errors.&lt;br /&gt;&lt;br /&gt;(expect ClassCastException (/12 0))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:19) : sample.test.core&lt;br /&gt;           (expect ClassCastException (/ 12 0))&lt;br /&gt;           (/ 12 0) did not throw ClassCastException&lt;br /&gt;Ran 1 tests containing 1 assertions in 4 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;There's another non-equality expectation that I do use fairly often - an expectation where the 'expected' value is a function. The following simple examples demonstrate that if you pass a function as the first argument to expect it will be called with the 'actual' value and it will pass or fail based on what the function returns. (&lt;a href="http://blog.jayfields.com/2011/02/clojure-truthy-and-falsey.html"&gt;truthy&lt;/a&gt; results pass, &lt;a href="http://blog.jayfields.com/2011/02/clojure-truthy-and-falsey.html"&gt;falsey&lt;/a&gt; results fail).&lt;pre&gt;(expect nil? nil)&lt;br /&gt;(expect true? true)&lt;br /&gt;(expect false? true)&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:19) : sample.test.core&lt;br /&gt;           (expect false? true)&lt;br /&gt;           true is not false?&lt;br /&gt;Ran 3 tests containing 3 assertions in 4 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;These are the majority of the non-equality expectations; however, there is one remaining non-equality expectation - in. Using 'in' is fairly straightforward, but since it has examples for vectors, sets, and maps I felt it deserved it's own blog post - coming soon.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-5329122265953925422?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/FrEbfBMwo_g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/5329122265953925422/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=5329122265953925422" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/5329122265953925422?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/5329122265953925422?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/FrEbfBMwo_g/clojure-non-equality-expectations.html" title="Clojure: Non-equality expectations" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QCQ389fip7ImA9WhRTEUk.&quot;"><id>tag:blogger.com,1999:blog-12467669.post-18279797059891742</id><published>2011-11-01T06:54:00.001-04:00</published><updated>2011-11-01T07:02:42.166-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-01T07:02:42.166-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="expectations" /><category scheme="http://www.blogger.com/atom/ns#" term="clojure" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><title>Clojure: expectations Introduction</title><content type="html">&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html"&gt;Clojure Unit Testing with Expectations Part One (this entry)&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-non-equality-expectations.html"&gt;Clojure Unit Testing with Expectations Part Two&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-with-values-in.html"&gt;Clojure Unit Testing with Expectations Part Three&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-and-doublenan.html"&gt;Clojure Unit Testing with Expectations Part Four&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-removing.html"&gt;Clojure Unit Testing with Expectations Part Five&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blog.jayfields.com/2011/11/clojure-expectations-unit-testing-wrap.html"&gt;Clojure Unit Testing with Expectations Part Six&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A bit of history&lt;/span&gt;&lt;br /&gt;Over a year ago &lt;a href="http://blog.jayfields.com/2010/09/clojure-another-testing-framework.html"&gt;I blogged&lt;/a&gt; that I'd written a testing framework for Clojure - &lt;a href="https://github.com/jaycfields/expectations"&gt;expectations&lt;/a&gt;. I wrote expectations to test my production code, but made it open source in case anyone else wanted to give it a shot. I've put zero effort into advertising expectations; however, I've been quietly adding features and expanding it's use on my own projects. At this point it's been stable for quite awhile, and I think it's worth looking at if you're currently using clojure.test.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Getting expectations&lt;/span&gt;&lt;br /&gt;Setting up expectations is easy if you use &lt;a href="https://github.com/technomancy/leiningen"&gt;lein&lt;/a&gt;. In your project you'll want to add:&lt;pre&gt;:dev-dependencies [[lein-expectations "0.0.1"]&lt;br /&gt;                   [expectations "1.1.0"]]&lt;/pre&gt;After adding both dependencies you can do a "lein deps" and then do a "lein expectations" and you should see the following output.&lt;pre&gt;Ran 0 tests containing 0 assertions in 0 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;At this point, you're ready to start writing your tests using expectations.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Unit Testing using expectations&lt;/span&gt;&lt;br /&gt;expectations is build with the idea that unit tests should contain &lt;a href="http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html"&gt;one assertion per test&lt;/a&gt;. A result of this design choice is that expectations has very minimal syntax.&lt;br /&gt;&lt;br /&gt;For example, if you want to verify the result of a function call, all you need to do is specify what return value you expect from the function call.&lt;pre&gt;(expect 2 (+ 1 1)&lt;/pre&gt;note: you'll want to (:use expectations); however, no other setup is required for using expectations. I created a sample project for this blog post and the entire test file looks like this (at this point):&lt;pre&gt;(ns sample.test.core&lt;br /&gt;  (:use [expectations]))&lt;br /&gt;&lt;br /&gt;(expect 2 (+ 1 1))&lt;/pre&gt;Again, we use lein to run our expectations.&lt;pre&gt;jfields$ lein expectations&lt;br /&gt;Ran 1 tests containing 1 assertions in 2 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;That's the simplest, and most often used expectation - an equality comparison. The equality comparison works across all Clojure types - vectors, sets, maps, etc and any Java instances that return true when given to Clojure's = function.&lt;pre&gt;(ns sample.test.core&lt;br /&gt;  (:use [expectations]))&lt;br /&gt;&lt;br /&gt;(expect 2 (+ 1 1))&lt;br /&gt;(expect [1 2] (conj [] 1 2))&lt;br /&gt;(expect #{1 2} (conj #{} 1 2))&lt;br /&gt;(expect {1 2} (assoc {} 1 2))&lt;/pre&gt;Running the previous expectations produces similar output as before.&lt;pre&gt;jfields$ lein expectations&lt;br /&gt;Ran 4 tests containing 4 assertions in 26 msecs&lt;br /&gt;0 failures, 0 errors.&lt;/pre&gt;Successful equality comparison isn't very exciting; however, expectations really begins to prove it's worth with it's failure messages. When comparing two numbers there's not much additional information that expectations can provide. Therefore, the following output is what you would expect when your expectation fails.&lt;pre&gt;(expect 2 (+ 1 3))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:4) : sample.test.core&lt;br /&gt;           (expect 2 (+ 1 3))&lt;br /&gt;           expected: 2 &lt;br /&gt;                was: 4&lt;/pre&gt;expectations gives you the namespace, file name, and line number along with the expectation you specified, the expected value, and the actual value. Again, nothing surprising. However, when you compare vectors, sets, and maps expectations does a bit of additional work to give you clues on what the problem might be. &lt;br /&gt;&lt;br /&gt;The following 3 expectations using vectors will all fail, and expectations provides detailed information on what exactly failed.&lt;pre&gt;(expect [1 2] (conj [] 1))&lt;br /&gt;(expect [1 2] (conj [] 2 1))&lt;br /&gt;(expect [1 2] (conj [] 1 3))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:5) : sample.test.core&lt;br /&gt;           (expect [1 2] (conj [] 1))&lt;br /&gt;           expected: [1 2] &lt;br /&gt;                was: [1]&lt;br /&gt;           2 are in expected, but not in actual&lt;br /&gt;           expected is larger than actual&lt;br /&gt;failure in (core.clj:6) : sample.test.core&lt;br /&gt;           (expect [1 2] (conj [] 2 1))&lt;br /&gt;           expected: [1 2] &lt;br /&gt;                was: [2 1]&lt;br /&gt;           lists appears to contain the same items with different ordering&lt;br /&gt;failure in (core.clj:7) : sample.test.core&lt;br /&gt;           (expect [1 2] (conj [] 1 3))&lt;br /&gt;           expected: [1 2] &lt;br /&gt;                was: [1 3]&lt;br /&gt;           3 are in actual, but not in expected&lt;br /&gt;           2 are in expected, but not in actual&lt;br /&gt;Ran 3 tests containing 3 assertions in 22 msecs&lt;br /&gt;3 failures, 0 errors.&lt;/pre&gt;In these simple examples it's easy to see what the issue is; however, when working with larger lists expectations can save you a lot of time by telling you which specific elements in the list are causing the equality to fail.&lt;br /&gt;&lt;br /&gt;Failure reporting on sets looks very similar:&lt;pre&gt;(expect #{1 2} (conj #{} 1))&lt;br /&gt;(expect #{1 2} (conj #{} 1 3))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:9) : sample.test.core&lt;br /&gt;           (expect #{1 2} (conj #{} 1))&lt;br /&gt;           expected: #{1 2} &lt;br /&gt;                was: #{1}&lt;br /&gt;           2 are in expected, but not in actual&lt;br /&gt;failure in (core.clj:10) : sample.test.core&lt;br /&gt;           (expect #{1 2} (conj #{} 1 3))&lt;br /&gt;           expected: #{1 2} &lt;br /&gt;                was: #{1 3}&lt;br /&gt;           3 are in actual, but not in expected&lt;br /&gt;           2 are in expected, but not in actual&lt;br /&gt;Ran 2 tests containing 2 assertions in 15 msecs&lt;br /&gt;2 failures, 0 errors.&lt;/pre&gt;expectations does this type of detailed failure reporting for maps as well, and this might be one if the biggest advantages expectations has over clojure.test - especially when dealing with nested maps.&lt;pre&gt;(expect {:one 1 :many {:two 2}}&lt;br /&gt;        (assoc {} :one 2 :many {:three 3}))&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:13) : sample.test.core&lt;br /&gt;           (expect {:one 1, :many {:two 2}} (assoc {} :one 2 :many {:three 3}))&lt;br /&gt;           expected: {:one 1, :many {:two 2}} &lt;br /&gt;                was: {:many {:three 3}, :one 2}&lt;br /&gt;           :many {:three with val 3 is in actual, but not in expected&lt;br /&gt;           :many {:two with val 2 is in expected, but not in actual&lt;br /&gt;           :one expected: 1&lt;br /&gt;                     was: 2&lt;br /&gt;Ran 1 tests containing 1 assertions in 19 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;expectations also provides a bit of additional help when comparing the equality of strings.&lt;pre&gt;(expect "in 1400 and 92" "in 14OO and 92")&lt;br /&gt;&lt;br /&gt;jfields$ lein expectations&lt;br /&gt;failure in (core.clj:17) : sample.test.core&lt;br /&gt;           (expect in 1400 and 92 in 14OO and 92)&lt;br /&gt;           expected: "in 1400 and 92" &lt;br /&gt;                was: "in 14OO and 92" &lt;br /&gt;            matches: "in 14" &lt;br /&gt;           diverges: "00 and 92" &lt;br /&gt;                  &amp;: "OO and 92"&lt;br /&gt;Ran 1 tests containing 1 assertions in 8 msecs&lt;br /&gt;1 failures, 0 errors.&lt;/pre&gt;That's basically all you'll need to know for using expectations to equality test. I'll be following up this blog post with more examples of using expectations with regexs, expected exceptions and type checking; however, if you don't want to wait you can take a quick look at the &lt;a href="https://github.com/jaycfields/expectations/blob/master/test/clojure/success/success_examples.clj"&gt;success tests&lt;/a&gt; that are found within the framework.&lt;div class="blogger-post-footer"&gt;&lt;br/&gt;&lt;a href="http://www.jayfields.com"&gt;&amp;copy; Jay Fields - www.jayfields.com&lt;/a&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/12467669-18279797059891742?l=blog.jayfields.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jayfields/mjKQ/~4/Rph_xV5OFoM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.jayfields.com/feeds/18279797059891742/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=12467669&amp;postID=18279797059891742" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/18279797059891742?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/12467669/posts/default/18279797059891742?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/jayfields/mjKQ/~3/Rph_xV5OFoM/clojure-expectations-introduction.html" title="Clojure: expectations Introduction" /><author><name>jaycfields</name><uri>http://www.blogger.com/profile/14491442812573747680</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="16" height="16" src="http://img2.blogblog.com/img/b16-rounded.gif" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.jayfields.com/2011/11/clojure-expectations-introduction.html</feedburner:origLink></entry></feed>

