<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
 <title>All About the Code</title>
 <link href="http://blog.knuthaugen.no//atom.xml" rel="self"/>
 <link href="http://blog.knuthaugen.no/"/>
 <updated>2022-10-03T08:55:03+02:00</updated>
 <id>http://blog.knuthaugen.no/</id>
 <author>
   <name>Knut Haugen</name>
   <email>knuthaug@gmail.com</email>
 </author>

 
 <entry>
   <title>The Clash of the Classnames</title>
   <link href="http://blog.knuthaugen.no//2022/10/classname-clashes"/>
   <updated>2022-10-02T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2022/10/classname-clashes</id>
   <content type="html">
&lt;p&gt;I recently came across a very sneaky bug in one of our projects using custom web components at work and I thought I’d document it for future google searches. The structure of the code is this: We have a shared base class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactiveElement&lt;/code&gt; which is, as the name implies, a class handling reacting to property and attribute changes, via several different callbacks abstracting some of the functionality of custom web components. The project in questions has a lot of components extending this class, specifying which properties and attributes it wants to have callbacks fired for. This is the very core of several projects and it works well.&lt;/p&gt;

&lt;p&gt;We also have a class called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualList&lt;/code&gt; that does caching, scrolling and handling large lists of items with some optimizations. We both use this VirtualList stand-alone and we have some specific list classes extending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualList&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And update in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactiveElement&lt;/code&gt; class broke this but only in a very specific way and only for certain use cases, namely the components extending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualList&lt;/code&gt;. And the update was in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactiveElements&lt;/code&gt; method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static get observedAttributes()&lt;/code&gt; which the browser calls on a class when it is registered as a custom element via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customeElementRegistry.define()&lt;/code&gt; method, to avoid it running twice for the same class, due to some server rendering functionality. But I digress.&lt;/p&gt;

&lt;p&gt;Time for some code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;VirtualListElement&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ReactiveElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;observedAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reactiveAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;listItems&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SpecificListElement&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;VirtualListElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;observedAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reactiveAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;shared&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Expected behaviour is that the class SpecificListElement should have both the property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shared&lt;/code&gt;and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listItems&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualListElement&lt;/code&gt; when it’s created. But it only ends up with the property &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;listItems&lt;/code&gt;. What gives?&lt;/p&gt;

&lt;p&gt;Turns out the fix in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactiveElement&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static get observedAttributes()&lt;/code&gt; now includes this code&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;observedAttributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_observedAttributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;which means that if the method has been called once for a class and its &lt;em&gt;static&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this._observedAttributes&lt;/code&gt; has been set, it won’t be set again. Remember this is a static method and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt; in this context is the class itself, not an instance. This fact, combined with that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualListElement&lt;/code&gt; was both used standalone &lt;em&gt;and&lt;/em&gt; as superclass, creates trouble. Deep in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualList&lt;/code&gt; code, was this code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;customElementRegistry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;virtual-list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;VirtualListElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So when that code was run first, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualListElement&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static get observedAttributes()&lt;/code&gt; is called first, that class gets its attributes set first. When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SpecificListElement&lt;/code&gt; comes along, the browser will call its observedAttributes method (in reality on the super class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactiveElement&lt;/code&gt;), but as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_observedAttributes_&lt;/code&gt; property doesn’t exists on the class itself, it will look up the inheritance chain, find it on  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VirtualListElement&lt;/code&gt; and then stop. The properties specified in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SpecificListElement&lt;/code&gt; will be ignored.&lt;/p&gt;

&lt;p&gt;The fix is quite simple. Instead of&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;customElementRegistry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;virtual-list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;VirtualListElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We do&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;customElementRegistry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;virtual-list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;VirtualListElement&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;to register the element with a anonymous class expression instead of a named class. You can also create any other named class extending the class used both as stand-alone element and superclass if you wish. A simple fix for a very confusing bug.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Effects of Automation</title>
   <link href="http://blog.knuthaugen.no//2019/08/the-effects-of-automation"/>
   <updated>2019-08-02T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2019/08/the-effects-of-automation</id>
   <content type="html">
&lt;p&gt;I want to reflect on the effects that automation can have on your daily life as a developer, after I came to think of the shift I have been through the last couple of years when it comes to deployments and habits and automation surrounding deployments. I dawned on me the other day, that deployments are now completely a non-event for me, and has been so for quite some time. I just don’t think about it anymore. In stark contrast to the extremely tired meme &lt;a href=&quot;https://twitter.com/hashtag/noDeployFriday&quot;&gt;#nodeployfriday&lt;/a&gt; circulating on twitter. How did that happen?&lt;/p&gt;

&lt;p&gt;Some context: I work in a team developing and maintaining about 20ish web applications. The team is small and fairly autonomous, at least when it comes to technical matters. My efforts into automating deployments and builds started around 2015-2016 at my previous employer (very similar in team size and technical stack) and the basis was a kind of semi-automated deploys but with few safeguards and the feelings involved in deployments where mixed. Deployments were talked about a lot, coordinated with several people and was a medium sized deal. Not huge, but definitely not small either. And the rule was don’t deploy on fridays (which is huge culture smell, maybe worth a whole post in itself). And deploys had to monitored.&lt;/p&gt;

&lt;p&gt;The end goal was of course to be able to cut down on lead times for putting code into production, with side goals of making it less of a pain to deploy, fewer failed deployments and fewer nervous faces when doing deployments.&lt;/p&gt;

&lt;h2 id=&quot;steps-to-safer-deployments&quot;&gt;Steps to safer deployments&lt;/h2&gt;

&lt;p&gt;There are several steps you can take to make your deployments more automated and safer, depending on the context you are in. Adding more unit and integration tests to the code, considering using feature toggles for new functionality, thinking carefully about the way the team communicates about the code (pair programming, code reviews, mob programming etc), using smoketests and possibly integration tests and performance tests before advancing the deploy from one stage to another and so on. Also, trying to deploy smaller chunks at a time contributes to safer deployments and can give you positive spiral effects. Deploying often (as you do with small changesets) is painful when deployment is a manual process. This leads to automation of the deployment process, which makes it easier to deploy, so you do it more often etc. etc.&lt;/p&gt;

&lt;p&gt;In our case we made use of integrated healthchecks in the applications (checked by the orchestration platform) and smoketests built into the deployment pipelines. The effects are that 1) the new version does not start when healthcheck fails (first gate) and if smoketests fail (second gate) deployments halts and is rolled back. If this is enough for you, depends on the context and the apps involved. First we automated the build of the apps and not the deployment. The deployment was manual, with only healthchecks when doing deployment. This made deploys much less of a worry, but still very much a topic of conversation. It took quite some time before everybody was comfortable with deploying on fridays (rule now was that you could deploy anytime, but be prepared to clean up afterwards if something went wrong). Gradually, deployments were not talked about anymore, but the decision to deploy was still a conscious one. The habit settled nicely, but I felt that we where missing out on some benefits in terms of automation.&lt;/p&gt;

&lt;h2 id=&quot;going-the-distance&quot;&gt;Going the Distance&lt;/h2&gt;

&lt;p&gt;This was the case when I switched employers and got the opportunity to start anew with more experience. My goal was to be able to deploy apps (at least some) directly to production on push to master. The apps in question here varied a lot in terms of importance, quality and scope. The discussion now became: “How much, and what, do we need to do in order to make this app deploy directly to production?”. That is profoundly different conversation to have, rather than “how often should we deploy” or “can we deploy on friday?”. A related question that can crop up in cases like this, is “how do we communicate changelogs to sales/marketing/users/whoever?”. This is not a topic you should skip! It’s important to get this right, but it’s also worth asking some questions about who uses the information in the changelogs and for what? But, automating the changelog is also possible via commits or other means.&lt;/p&gt;

&lt;p&gt;So, automating deploys to production: how to achieve it? First try to determine if this is at all wanted in your context. Web development can more or less always achieve this, but there are a lot of other types of software where the work involved may not match the perceived benefit. This is an important question. But let’s say you want to go that way, here are some tips:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Take small steps&lt;/li&gt;
  &lt;li&gt;Make build and deploy errors discoverable (build status to dashboard, slack etc)&lt;/li&gt;
  &lt;li&gt;Make errors clear and understandable&lt;/li&gt;
  &lt;li&gt;make the log clear to read (see previous point)&lt;/li&gt;
  &lt;li&gt;Use version control for your build pipelines and infrastructure&lt;/li&gt;
  &lt;li&gt;add tests to important parts first&lt;/li&gt;
  &lt;li&gt;add health checks&lt;/li&gt;
  &lt;li&gt;add smoketests&lt;/li&gt;
  &lt;li&gt;make deployments visible in dashboard with application metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with auto-deploy to test environment, build out unit tests, integration tests and other tests as needed. Gain confidence in the application and deploymentes. Measure how often builds and deploys to that env fails, if possible display it on a graph that everybody can see. Start deploying automatically to staging environment og pre-production depending on what you have. Gain more confidence - rince and repeat. Gain confidence also in the build and deployment infrastructure itself. If it fails you, improve it.&lt;/p&gt;

&lt;p&gt;This confidence builds slowly. Let it accrue over time, don’t force it. Especially let team members not interested or unexperienced in the tao of deploys take part, improve and use it. This is important, since when deploys are automatic, nobody (and everybody) will have to take action when something goes wrong.&lt;/p&gt;

&lt;p&gt;As deployments are made increasinly more automatic, they just &lt;em&gt;fade&lt;/em&gt; from your mind and if you’re not paying attention to this you might not realize it. The chatter about deploys disappear from the workplace and things just happen. The point of being able to see the deploys marked on metric graphs (see &lt;a href=&quot;/2015/09/deploys-in-graphs.html&quot;&gt;my post on deploy overlays&lt;/a&gt;) is important when deploys are automatic, to be able to catch when a deploy introduces some weird behaviour in an application.&lt;/p&gt;

&lt;p&gt;For me, the end result of this is a more relaxed work situation where changes flow from development to production. No deployment anxiety, no periods where deployment isn’t allowed and we can spend our time focusing on other things rather than talk about deployments. I like it this way.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Latest is such a bad idea</title>
   <link href="http://blog.knuthaugen.no//2018/05/latest-is-a-bad-idea"/>
   <updated>2018-05-29T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2018/05/latest-is-a-bad-idea</id>
   <content type="html">
&lt;p&gt;For the second time I have found myself pulling what little hair I have left out because of people falling for the trap that is using “latest” as version number in a dependency, on the web specifically. This is such a bad idea, but people keep falling into the pit of using it. I can understand that, because it is very alluring and simple. “Just use latest, and you’re always up-to-date. No need to keep track of releases and just code and be happy.” Understandable reasons, but the downsides are huge.&lt;/p&gt;

&lt;p&gt;Most of these points refer to using an asset via an url in the markup of your site (maybe developed by another in-house team, or someone else entirely), but they apply to lesser extent to build-time dependencies too.&lt;/p&gt;

&lt;h3 id=&quot;low-ttl&quot;&gt;Low TTL&lt;/h3&gt;

&lt;p&gt;Using latest as the version number almost always require low cache times for the asset. After all, if you cache it for a long time, you would not get the benefits of latest and get the newest version. Low cache time hurts your google-fu and pagerank and contributes to making your site slower for users. Don’t do that.&lt;/p&gt;

&lt;h3 id=&quot;undetected-dependency-changes&quot;&gt;Undetected dependency changes&lt;/h3&gt;

&lt;p&gt;This is the flipside of the “feature” of “latest” as it updates your dependencies without you knowing. Great feature, right? Yeah, when all goes well it is. When someone botches the build and pushes a bad version you get blindsided with a failing site, with no warning. This happened to us recently. The core team released a botched build and it hit our site 10 minutes before the whole team was leaving for summer party. 1 hour later an everyone would be well into their beers. All because a guy on the team thought using “latest” was the simplest way forward. It was for a time, but it caught up to us.&lt;/p&gt;

&lt;p&gt;Don’t botch the build, you say? Well, good luck in never having human errors occur.&lt;/p&gt;

&lt;h3 id=&quot;which-version-are-we-actually-running&quot;&gt;Which version are we actually running?&lt;/h3&gt;

&lt;p&gt;Which version is the latest? Was the last upgrade a patch, a minor or a major? Does the release warrant changes to our site because of non-backward compatible changes? How do you find out, without having some sort of relationship with the version number? Ye gods the trouble that can come from this.&lt;/p&gt;

&lt;h3 id=&quot;reproducible-builds&quot;&gt;Reproducible builds&lt;/h3&gt;

&lt;p&gt;When talking build-time dependencies, the concept of reproducible builds is important in devops, to be able to recreate a build later in time, from a git version. This is hard to get 100%, but simple steps will take you a long way towards it. This absolutely requires having specific version numbers in dependency management systems. Even version ranges, which Node users love, makes this near impossible. What version did “latest” resolve to at build time? How would you even begin to find out what the diff was between the two builds where “latest” resolved to two different versions? Using “latest” in e.g. node dependencies will create bugs for you, it’s just a matter of time and how bad the bug will be. This goes even for dev dependencies, even though the repercussions are smaller. Gemfile.lock and package-lock.json does take some steps to avoid falling into this trap, and when you commit those files to version control, you can reproduce the builds back in time. But even so, I would argue that discreet version numbers are easier to manage and above all read and understand. It is right there, without having to check other files.&lt;/p&gt;

&lt;p&gt;Using dynamic versions like latest, in the early stages of development is easy and should be fine, but you run the risk of forgetting to fix them later.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Kubernetes Wars: Day 34</title>
   <link href="http://blog.knuthaugen.no//2016/12/the-kubernetes-wars-day-34"/>
   <updated>2016-12-25T00:00:00+01:00</updated>
   <id>http://blog.knuthaugen.no//2016/12/the-kubernetes-wars-day-34</id>
   <content type="html">
&lt;h4 id=&quot;other-post-in-the-series&quot;&gt;Other post in the series&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-0.html&quot;&gt;Kubernetes Wars Day 0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-3.html&quot;&gt;Kubernetes Wars Day 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-7.html&quot;&gt;Kubernetes Wars Day 7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Operation k8s log, day 34 1100 Zulu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We got used to this kind of fighting after a while, you ease into it and accept it as the new standard operating
procedure. More and more apps where moved to the kubernetes stack and things where running smoothly. But, when you think to yourself «hmm, I haven’t gotten any surprises lately?» that’s when they hit you in the face.&lt;/p&gt;

&lt;h2 id=&quot;memory-who-needs-it&quot;&gt;Memory? Who needs it?&lt;/h2&gt;

&lt;p&gt;As more java apps where being moved whe started out by duplicating the memory settings for the old app, e.g. -Xmx 1024mb on the command line for the JVM. And then setting the memory limit for the container to 2-300 MB more than that. This works fine. Sometimes. For a while.&lt;/p&gt;

&lt;p&gt;But then someone notices a weird high restart count on one of the apps. Not so much that the alarms go off, but maybe a couple of times a day or a handful a week. So someone slaps some more memory on it, but then stops to think «This container eats 300 megs &lt;em&gt;more&lt;/em&gt; than the Xmx setting of the JVM in the container?» That’s a little strange isn’t it? What is actually going on here?&lt;/p&gt;

&lt;p&gt;If you ask linux what memory the process is using, it will answer in (usually) RSS (Resident Set Size), which for java roughly translates to Heap Size + Metaspace + offheap size. Here, Offheap size consists of thread stacks, direct buffers and mapped files (libraries and jars), plus the jvm code itself. MetaSpace contains class instances in the code that runs. This all adds up.&lt;/p&gt;

&lt;p&gt;The big eater in the family is the threads which as a default has 1M stack size. Some of our apps has a lot to do and can get between 500-1000 threads per instance in peak traffic. That’s a gig of memory right there.&lt;/p&gt;

&lt;p&gt;And the mapped libraries and jvm eats some more. So the answer is: measure it properly to find out just how much memory your app needs in a docker container to stay alive and thrive. It’s more than you think. A nice guide tomeasuring and understanding memory in java is &lt;a href=&quot;http://trustmeiamadeveloper.com/2016/03/18/where-is-my-memory-java/&quot;&gt;http://trustmeiamadeveloper.com/2016/03/18/where-is-my-memory-java/&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;cpu-who-needs-it&quot;&gt;CPU? Who needs it?&lt;/h2&gt;

&lt;p&gt;And then CPU came along. Based on the rough estimates of CPU requirements each app got a handlful and made do. This usually works. But now and then, somebody working on java apps said that their apps would just get killed a short while after startup, with no apparent reason. What’s happening here? After some digging, it turns out that the app was being killed by kubernetes itself, roughly 30s after starting, not by OOM but by not replying OK on liveness and readyness probes. The app in question is a low traffic app with a 2 CPU core limit. Shouldn’t that be enough when normal traffic barely uses half a core?&lt;/p&gt;

&lt;p&gt;Well, these apps where running spring boot, spring frameworks less (but only a little) ugly cousin. Less ugly because it’s a modern framework, but still ugly because, well, it’s spring (YMMV). Turns out that all the housekeeping spring wants to do on startup takes a lot of time when all you give it is two cpu cores. The outcome is that it won’t start in time for the readyness/liveness probes complete in time and kubernetes determines this app is dead and finish it off. So there’s two fixes, increase the initialWait for the liveness/readyness checks or boost the cpu limit way higher. I like speedy deploys, so we use the latter strategy.&lt;/p&gt;

&lt;h2 id=&quot;measurements&quot;&gt;Measurements&lt;/h2&gt;

&lt;p&gt;We measure cpu and memory utilization (with the requirement and limit in the graph) per app via heapster/influxdb and display them in grafana alongside our other graphs. This is a great way to see how your app is doing and tuning the limits to suit the app. This is really hard without visualisation in my experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;End log Operation k8s, day 34&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Reading Files in Bash</title>
   <link href="http://blog.knuthaugen.no//2016/06/reading-files-in-bash"/>
   <updated>2016-06-15T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2016/06/reading-files-in-bash</id>
   <content type="html">
&lt;p&gt;I came across a small but interesting difference in ways of reading files in bash, and specifically processing a file line by line.&lt;/p&gt;

&lt;p&gt;I had a small function to read property files and splitting each line in a key and a value and suddenly a bug cropped up. This worked fine when reading existing property files. But when introducing a space right of the =, it failed. It made two “lines” out of “foo=bar zoot”, while one line out of “foo=bar”.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;line &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&amp;lt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s our old friend IFS (internal field separator) at work here, with its default value of “&lt;space&gt;&lt;tab&gt;&lt;newline&gt;&quot; in bash. So we unset it and bob's our uncle.&lt;/newline&gt;&lt;/tab&gt;&lt;/space&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;prev_ifs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;line &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&amp;lt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prev_ifs&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(It’s nice to preserve the value of IFS and reset it after you’re done. If you’r in a function you can of course local it instead)&lt;/p&gt;

&lt;p&gt;This code works fine without any IFS twiddling, though:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; line&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &amp;lt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are manny more ways to read files in bash of course. The lesson is of course that I should have tested this better when I first wrote it :-)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Kubernetes Wars: Day 7</title>
   <link href="http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-7"/>
   <updated>2016-06-12T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-7</id>
   <content type="html">
&lt;h4 id=&quot;other-post-in-the-series&quot;&gt;Other post in the series&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-0.html&quot;&gt;Kubernetes Wars Day 0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-3.html&quot;&gt;Kubernetes Wars Day 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/12/the-kubernetes-wars-day-34.html&quot;&gt;Kubernetes Wars Day 34&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Operation k8s log, day 7 0800 Zulu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Along came the day for deploying the first high traffic node app to Kubernetes. And with, our pain started to creep in. I had deployed around 10 apps earlier, and everything worked out smoothly. These were small helper apps doing background requests and not seeing a lot of traffic.&lt;/p&gt;

&lt;p&gt;Confidence was high and I went straight to the template app getting most of the browser traffic for our site, probably 10-20 rather heavy requests a second and running 6 instances in production in the old docker setup. So I deployed with six replicas and switched the backends in varnish. I takes a little while for content to go stale but I checked a url with a cache-buster and it did nothing. A b s o l u t e l y nothing, it just hanged there. WTF? Checked the logs real quick and there where 500s everywhere. Luckily the varnish grace/saint code saved my bacon while I switched back and nobody really noticed. But what the hell?&lt;/p&gt;

&lt;p&gt;I was seeing timeouts on requests to almost every other server it was talking to for content, config, ads and what have you. What was this? Seriously overloaded server? It barely used cpu and ram, but somehow was not able to cope with almost &lt;em&gt;any&lt;/em&gt; traffic. Some testing revealed it maxed at about 1 request per second.&lt;/p&gt;

&lt;p&gt;So we started what became the biggest yak shave of the month, looking at every possible angle to find out what these instances was doing to make them go so slow. Was it IO (some static files where read from disk)? Nope, not much IO going on. Was it slow IO performance in the docker container? Nope, barely reading anything either. We tried increasing the ram and the CPU for the container, just to rule that out to no avail.&lt;/p&gt;

&lt;p&gt;I even tried to add two kubernetes instances in addition to the old running ones, and they still crashed soon after start. We scratched our heads again and again.&lt;/p&gt;

&lt;h2 id=&quot;dns-mofo&quot;&gt;DNS mofo&lt;/h2&gt;

&lt;p&gt;And so one of our ops guys stumbled on the solution. The traffic was generating several thousand dns requests a second, for calling other services and sending metrics. DNS resolution in node is a bit naive, you could say and default it will look up the address in use &lt;em&gt;every&lt;/em&gt; time it should be called, with no regard for TTL or caching. And in our kubernetes clusters we are running skydns for some nice features and since we also used an RC template not specifying a dns policy, we got &lt;em&gt;ClusterFirst&lt;/em&gt;. This meant that the also under-cpu’ed skydns instances were getting hammered with every single one of those severalt thousand dns resolves every second, peaking on cpu and returning slower and slower responses. This made the node event loop wait increasingly longer for replies and in the end everything slowed to a crawl.&lt;/p&gt;

&lt;p&gt;So the fix was to set dns policy to &lt;em&gt;Default&lt;/em&gt; which means using the docker dns setup. In our setup that meant dnsmasq running locally on all nodes, which we in hindsight should have been running all along. You learn something every day, eh?&lt;/p&gt;

&lt;p&gt;And with that, everyhing just cleared up and we could serve all the traffic (and some more, but that is a tale for another time) on the same number of instances in kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;End log Operation k8s, day 7&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Kubernetes Wars: Day 3</title>
   <link href="http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-3"/>
   <updated>2016-06-04T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-3</id>
   <content type="html">
&lt;h4 id=&quot;other-post-in-the-series&quot;&gt;Other post in the series&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-0.html&quot;&gt;Kubernetes Wars Day 0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-7.html&quot;&gt;Kubernetes Wars Day 7&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/12/the-kubernetes-wars-day-34.html&quot;&gt;Kubernetes Wars Day 34&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Operation k8s log, day 3 1300 Zulu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Day three and we have treated the wounded and regrouped after our initial encounter with the enemy. 
There are yaks everywhere, get your razors ready, boys!&lt;/p&gt;

&lt;h2 id=&quot;config&quot;&gt;Config&lt;/h2&gt;

&lt;p&gt;How to deal with configuration? Kubernetes likes app config in environment variables, not config files. This is easy in our node apps using convict, pretty easy in our ruby apps and ranging from relatively easy to bloody hard in our java apps. But how to get config into the replication controllers? We opted for using configmaps (a kubernetes object) to store the config, reference the variables from the rc files and maintain it in git controlled files. So when we want to change to app config, update the config files and run a script which updates the configmap and reloads all the pods for the app. Incidentally, the way we do that, is to delete them, and let kubernetes recreate them. Don’t do this if you run one cluster ;-) We &lt;em&gt;should&lt;/em&gt; make the apps read the config automatically, but since none our apps do that, we needed a solution that works now.&lt;/p&gt;

&lt;p&gt;This also means we can have separate config for different environments while the file definition of the RC remains the same. We started out with only the configuration variables external to the RC file, but soon realized we needed to externalize the version of the image, the replica number and the limits too.&lt;/p&gt;

&lt;p&gt;This has worked really well so far.&lt;/p&gt;

&lt;p&gt;The deploy script which either creates everything on the first deploy (from yaml files) or performs a rolling upgrade from one version to another. The deploy script also need to handle the configmap and substituting the values from external files.&lt;/p&gt;

&lt;style&gt; code.language-bash { font-size: 70% }&lt;/style&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /usr/local/amedia-tools/dev/dev_functions.sh
&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/k8s-files&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/bin/kubectl&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--kubeconfig=/etc/kubernetes/config&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$# &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-lt&lt;/span&gt; 2 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usage: k8sdeploy APP ENV [VERSION]&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Deploy a new version of an app to the specified environment. In production, one cluster at a time.&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prod&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; snap&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

function &lt;/span&gt;deploy_from_file&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; rc &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; Error&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;warnlog &lt;span class=&quot;s2&quot;&gt;&quot;RC for &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; already exist, will not try to re-create from yaml files again&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
    
    if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yaml_dir&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create namespace &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; 
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;        
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yaml_dir&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-svc&lt;/span&gt;.yaml

        &lt;span class=&quot;c&quot;&gt;#before we create the rc, expand variables from resources file&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#make a tmp file for the newly generated rc file&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;rcfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;expand_placeholders_from_resources &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-rc&lt;/span&gt;.yaml &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; 
        
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rcfile&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;#if test or snapshot, scale down to 1 replica&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; scale &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--replicas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 rc &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fi
    else
        &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Could not find yaml files for app &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; in &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$yaml_dir&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;rolling_update&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;#check if the service is there&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; rc &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; Error&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;deploy_from_file &lt;span class=&quot;nv&quot;&gt;$dc&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#update configmap first&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; delete configmap config &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
            &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Error occured when refreshing configmap for app. See error below&quot;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$out&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
        &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
        
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; rolling-update &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--update-period&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1s &lt;span class=&quot;nt&quot;&gt;--poll-interval&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2s &lt;span class=&quot;nt&quot;&gt;--timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2m &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dr.api.no/amedia/&lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$version&lt;/span&gt; 

        &lt;span class=&quot;c&quot;&gt;#if successful, patch resource file with new image version&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; rc &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image:&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2-3&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;:&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;#patch image version into resources file&lt;/span&gt;
            patch_file &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.resources &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else
            &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Rolling-update failed. Inspect the status of the app with k8sstatus &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; and dig from there&quot;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
        &lt;span class=&quot;k&quot;&gt;fi
    fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#update k8s-files repo&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yaml_dir&lt;/span&gt;
git pull

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Could not pull the k8s-files repo. Please check permissions on jump.api.no:/usr/local/k8s-files&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; - &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1

&lt;span class=&quot;c&quot;&gt;#generate configmap&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;generate_configmap_file &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$version&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; prod&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        for &lt;/span&gt;dc &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;osl2 osl3 ksd1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
            &lt;/span&gt;rolling_update &lt;span class=&quot;nv&quot;&gt;$dc&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;done
    elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;rolling_update &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;rolling_update snapshot &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi
else&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;#first deploy, we need the yaml&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; prod&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;deploy_from_file osl2
        deploy_from_file osl3
        deploy_from_file ksd1
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;deploy_from_file &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;deploy_from_file snapshot
    &lt;span class=&quot;k&quot;&gt;fi
fi

&lt;/span&gt;report_deploy &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then the script for &lt;em&gt;just&lt;/em&gt; updating the config of an app, without deploying anything.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /usr/local/amedia-tools/dev/dev_functions.sh
&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/k8s-files&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/bin/kubectl
&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--kubeconfig=/etc/kubernetes/config&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usage: k8sconfig APP ENV&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Update the config for an application. This script will pull k8s-files, convernt the env.properties file to a kubernetes configmap, and restart all pods to read the new config.&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;port_for_app &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;prod&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; snap&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi


function &lt;/span&gt;create_config_from_etcd&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; configmap config 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; Error&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#config map is there, delete it and recreate&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; delete configmap config &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; create &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Something bad happened and we could not create configmap. Aborting restart&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;#do a replace on the rc, to accomodate for new variables in the yaml&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# first, patch the rc file to the latest (running version)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; rc &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image:&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2-3&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;:&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;#patch image version into resources file&lt;/span&gt;
    patch_file &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.resources &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;image&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;#make a tmp file for the newly generated rc file&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;rcfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;expand_placeholders_from_resources &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-rc&lt;/span&gt;.yaml &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; 

    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; replace &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rcfile&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;rcfile&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;#if test or snapshot, scale down to 1 replica&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; scale &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--replicas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 rc &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
    
    &lt;span class=&quot;c&quot;&gt;# delete existing pods, to recreate and read new config&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;had_num&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get pods &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; podip | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;: | &lt;span class=&quot;nb&quot;&gt;wc&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    infolog &lt;span class=&quot;s2&quot;&gt;&quot;stopping running pod instance(s) in cluster=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;had_num&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; instance(s) found running)&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; delete &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; pod

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-ne&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BOLD&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INFO_STYLE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RESET&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Checking if they are back up (checking in intervals, max 150s): &quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
    &lt;span class=&quot;nv&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;10
    are_pods_up &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;had_num&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-le&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$END&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-eq&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;i &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; i + 1&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-gt&lt;/span&gt; 5 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
            &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;20s
        &lt;span class=&quot;k&quot;&gt;else
            &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;10s
        &lt;span class=&quot;k&quot;&gt;fi
        &lt;/span&gt;are_pods_up &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;had_num&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# still on status 0 means end of loop but instance not up ag&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-eq&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Error: Pods seem unable to restart. Please check their status in &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; manually. We need &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;had_num&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; running per server center. Found &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; running instances&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;else 
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Yay! Back up.&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#we need the yaml&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
git pull 

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;errlog &lt;span class=&quot;s2&quot;&gt;&quot;Could not pull the k8s-files repo. Please check permissions on jump.api.no:/usr/local/k8s-files&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; - &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1

&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;generate_configmap_file &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;yaml_dir&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; prod&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;create_config_from_etcd osl2 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    create_config_from_etcd osl3 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    create_config_from_etcd ksd1 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;create_config_from_etcd &lt;span class=&quot;nv&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;create_config_from_etcd snapshot &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;configmap_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(Yeah, it could do with some refactoring)&lt;/p&gt;

&lt;h2 id=&quot;container-metrics&quot;&gt;Container Metrics&lt;/h2&gt;

&lt;p&gt;The question that popped up was: when do we know the cluster is running out of resources, and preferably &lt;em&gt;before&lt;/em&gt; the deploy fails with events saying you’re shit out of memory or cpu? Container metrics to the rescue.&lt;/p&gt;

&lt;p&gt;We have a existing metric system backed in graphite which has worked well for us. We have used the host name as the metric path to give us the oppurtunity to isolate metrics per host. When kubernetes manages the containers, the host on which it runs becomes ever changing. to solve this, we landed on using &lt;a href=&quot;https://github.com/kubernetes/heapster&quot;&gt;heapster&lt;/a&gt; for gathering container metrics, and then storing them in &lt;a href=&quot;https://influxdata.com/&quot;&gt;influxdb&lt;/a&gt;. The influx query language gives easy ways to abstract away over changing host names. I am seeing use of influxdb for app metrics in the future too, for the same reason.&lt;/p&gt;

&lt;p&gt;Next time on the kubernetes wars: the curious case of the slow node apps.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;End log Operation k8s, day 3&lt;/em&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Kubernetes Wars: Day 0</title>
   <link href="http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-0"/>
   <updated>2016-06-02T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2016/06/kubernetes-wars-day-0</id>
   <content type="html">
&lt;h4 id=&quot;other-post-in-the-series&quot;&gt;Other post in the series&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-3.html&quot;&gt;Kubernetes Wars Day 3&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/06/kubernetes-wars-day-7.html&quot;&gt;Kubernetes Wars Day 7&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2016/12/the-kubernetes-wars-day-34.html&quot;&gt;Kubernetes Wars Day 34&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Operation k8s log, day 0 1100 Zulu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, we are sent out on a mission to deploy Kubernetes in our data centers, Google wrote it, management want it and the ops guys are gagging for it. And we have no choice but to follow orders and start using the damn thing.&lt;/p&gt;

&lt;p&gt;So what is this beast we are about to tackle? Anything like running 15-node elasticsearch clusters with bad sharding policies and crappy index patterns? Or as bad as trying to make sense of an apache cassandra cluster without knowing much about native american tribes? Well, we are about to find out.&lt;/p&gt;

&lt;p&gt;We have been running docker containers in production for a while now, and ops complained about lack of control on resources and limits and developers complained about lack of orchestration and scheduling. After a small firefight with the sales department on docker.com, who tried their probably-best-but-not-so-very-good to sell us some enterprisey stuff, along came kubernetes as the most likeley bogey we had to face.&lt;/p&gt;

&lt;h2 id=&quot;so-what-is-kubernetes&quot;&gt;So What is Kubernetes?&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://kubernetes.io&quot;&gt;Kubernetes&lt;/a&gt; is container orchestration, deployment, management and scaling, simply put. And boy can it do a lot for you. Take the kubectl cli, with its 48 commands and a grand total of 22 shared command line options and about 10-12 options &lt;em&gt;per command&lt;/em&gt; it’s a whopper of a system. So what to do?&lt;/p&gt;

&lt;p&gt;Bash, of course.&lt;/p&gt;

&lt;p&gt;Write bash (or any other scripting language) to abstract away the forest of options you need for &lt;em&gt;every&lt;/em&gt; command, like cluster (one per data center, more on that later) and namespace (more on that too), object name to operate on and so on. You will need an abstraction. I also wanted to fit k8s deployment and management into existing scripts dealing with both straight docker and old school native servers so a separate set of k8s scripts was born. I opted for one per command instead of the k8s style with one big ass one with a million options, because that’s how I roll.&lt;/p&gt;

&lt;h2 id=&quot;namespaces-clusters-and-stuff&quot;&gt;Namespaces, Clusters and stuff&lt;/h2&gt;

&lt;p&gt;Kubernetes docs says to be vary about multi location clusters so we opted for one cluster per data center, three in total. This gives a bit of overhead when it comes to deployment (we can’t deploy in one big batch, but need three deploys to update all pods) but at the same time we can take a whole data center offline and upgrade the cluster without affecting the other two, which is nice.&lt;/p&gt;

&lt;p&gt;Another thing we discovered was that kubernetes by default make configuration and variables available to all containers in a namespace and everyone could see everything when all apps were deployed in the default namespace. So we namespace all our apps into a separate namespace named after the app. As longs as all commands are namespaced (hence wrapper scripts) there is not much hassle. For snapshot and test environments, we use one cluster and not three to keep things simpler.&lt;/p&gt;

&lt;h2 id=&quot;scripts-scripts-and-more-scripts&quot;&gt;Scripts, scripts and more scripts&lt;/h2&gt;

&lt;p&gt;We’ll cover all the scripts in later blog posts, but let’s start. I asked myself the question: what information about a running app do I need to get out of kubernetes most often? In our older stacks, we have hade a ping command which pinged the in-app ping endpoints and gave us a lot of information: where the app is running, what version it is running and which port. Could I get this from kubernetes?&lt;/p&gt;

&lt;p&gt;The result was actually two scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;k8sstatus APP ENV&lt;/strong&gt; gives you this:&lt;/p&gt;
&lt;style&gt; code.language-bash { font-size: 65% }&lt;/style&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt; 
osl2:
NAME        DESIRED   CURRENT   AGE       CONTAINER&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;   IMAGE&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;                           SELECTOR
manifesto   1         1         44d       manifesto      dr.api.no/amedia/manifesto:0.0.3   &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manifesto
NAME              READY     STATUS    RESTARTS   AGE       NODE
manifesto-lxf9x   1/1       Running   3          15d       docker006-osl2
&lt;span class=&quot;nt&quot;&gt;-----------------------------------&lt;/span&gt;
osl3:
NAME        DESIRED   CURRENT   AGE       CONTAINER&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;   IMAGE&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;                           SELECTOR
manifesto   1         1         44d       manifesto      dr.api.no/amedia/manifesto:0.0.3   &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manifesto
NAME              READY     STATUS    RESTARTS   AGE       NODE
manifesto-uwu4l   1/1       Running   0          2d        docker009-osl3
&lt;span class=&quot;nt&quot;&gt;-----------------------------------&lt;/span&gt;
ksd1:
NAME        DESIRED   CURRENT   AGE       CONTAINER&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;   IMAGE&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;S&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;                           SELECTOR
manifesto   1         1         44d       manifesto      dr.api.no/amedia/manifesto:0.0.3   &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manifesto
NAME              READY     STATUS    RESTARTS   AGE       NODE
manifesto-8q4f7   1/1       Running   0          16d       docker008-ksd1
&lt;span class=&quot;nt&quot;&gt;-----------------------------------&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;/bin/bash

&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/bin/kubectl&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--kubeconfig=/etc/kubernetes/config&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usage: k8sstatus APP ENV&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Display status for an app, both Replication controller and pods&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi

function &lt;/span&gt;list_pods&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get pods &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-----------------------------------&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#default is prod&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; prod&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;osl2: &quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get rc &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;osl2 &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    list_pods &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; osl2 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;osl3: &quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get rc &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;osl3 &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    list_pods &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; osl3 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ksd1: &quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get rc &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ksd1 &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    list_pods &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; ksd1 &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get rc &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    list_pods &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get rc &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;snapshot &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wide
    list_pods &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; snapshot &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which tells us first the status of the Replication Controller for the app (and the version) and then iterates through all pods for this RC and shows where these are running and status for them. What this doesn’t tell us, is the actual reply from the ping endpoint in the app, and not which IP it is running on (not always needed in kubernetes, but nice to test against when debugging. Thus k8sping was born.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt; 
k8sping manifesto prod
http://10.30.6.7:9644/manifesto/apiadmin/ping OK 0.0.3 master-undefined
http://10.31.9.9:9644/manifesto/apiadmin/ping OK 0.0.3 master-undefined
http://10.29.8.13:9644/manifesto/apiadmin/ping OK 0.0.3 master-undefined&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And whammo, port and ip for the individual pods, and the return value from the ping endpoint.&lt;/p&gt;

&lt;p&gt;Source:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/usr/local/bin/kubectl&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;--kubeconfig=/etc/kubernetes/config&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usage: k8sping APP ENV&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Ping application ping endpoint the pods, all instances.&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;port_for_app &lt;span class=&quot;nv&quot;&gt;$app&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#echo &quot;Deploying $app $version on jump.api.no&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;ping_app&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;

    &lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;$'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;ips&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kube_opt&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; get pods &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; podip | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;: | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/\s//g'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;ip &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ips&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; http://&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/apiadmin/ping&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/apiadmin/ping &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; prod&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;dc &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;osl2 osl3 ksd1&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
        &lt;/span&gt;ping_app &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dc&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done
elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
    ping_app &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    ping_app snapshot &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Kubectl command output is a joy to parse. Everything (well, almost everything) makes sense.&lt;/p&gt;

&lt;p&gt;That’s enough for one day, this is Gordon, signing off :-)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;End log Operation k8s, day 0&lt;/em&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Talk Friday: Microservice Architecture</title>
   <link href="http://blog.knuthaugen.no//2016/03/talk-friday-microservice-architecture"/>
   <updated>2016-03-11T00:00:00+01:00</updated>
   <id>http://blog.knuthaugen.no//2016/03/talk-friday-microservice-architecture</id>
   <content type="html">
&lt;p&gt;I quick post recommending a good talk on microservice architecture by Neal Ford from the JavaZone conference
last year. He touches on the implications of a microservice architecture and the how and why of it. Lots of details and a broad coverage, delivered in usual neal Ford style. Worth a watch if you’re about to jump on the microservice bandwagon.&lt;/p&gt;

&lt;p&gt;Why this talk now? Because I just saw it now ;-)&lt;/p&gt;

&lt;iframe src=&quot;https://player.vimeo.com/video/138956644&quot; width=&quot;500&quot; height=&quot;281&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;a href=&quot;https://vimeo.com/138956644&quot;&gt;Building Microservice Architectures - Neal Ford&lt;/a&gt; from &lt;a href=&quot;https://vimeo.com/javazone&quot;&gt;JavaZone&lt;/a&gt; on &lt;a href=&quot;https://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Deploy Overlay in Metric Graphs</title>
   <link href="http://blog.knuthaugen.no//2015/09/deploys-in-graphs"/>
   <updated>2015-09-10T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2015/09/deploys-in-graphs</id>
   <content type="html">
&lt;p&gt;I got a question about this setup on twitter and I thought I’d explain this is in a blog post instead of 140 char twitter messages (which is a pain).&lt;/p&gt;

&lt;p&gt;We do quite a lot of metrics in our apps, which are nice for debugging and seeing what is going on. But one thing we have found very handy is to see when deploys to production happen (and they can happen up to 5-10 times a day in various apps, so without a graph it can be very hard to see which deploy made the response time shoot through the roof, or the rate of 500 errors skyrocket.&lt;/p&gt;

&lt;p&gt;We use graphite to collect the metrics which are most often sent via the &lt;a href=&quot;https://github.com/dropwizard/metrics&quot;&gt;dropwizard metrics framework&lt;/a&gt; (which is excellent) and &lt;a href=&quot;http://grafana.org/&quot;&gt;Grafana&lt;/a&gt; to present pretty graph dashboards to the user.&lt;/p&gt;

&lt;p&gt;First step is to record the deploy. From the deploy scripts, each production deploy runs this little command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%s&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;deploys.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | nc &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$graphite&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-host&lt;/span&gt; 2003&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Where NAME is the app name, the value sent is 1 and then the timestamp.&lt;/p&gt;

&lt;p&gt;Second we use the annotations feature in grafana which makes it easy to toggle this on and off to draw vertical lines for all timestamps where the value is 1. In raw graphite, this is the drawAsInfinite() function. The nice thing grafana adds here, is mouse over boxes with more info. The result looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/deploy-overlay.png&quot; width=&quot;800&quot; height=&quot;404&quot; alt=&quot;Deploys overlaid on 4xx responses graph&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The mouse over is the killer feature and shows us that the app orwell was deployed at 2015-09-09 15:36. If this deploy had caused a spike in the graph, it would be immediately visible. This has saved us a lot of digging on more than one occasion not to mention alerted us to the fact that somebody (which shall remain nameless) introduced javascript which double the page rendering time of our front pages :-).&lt;/p&gt;

&lt;p&gt;For more info about our metrics appproach, see my earlier &lt;a href=&quot;http://blog.knuthaugen.no/2013/10/the-metrics-initiative/&quot;&gt;post on the subject&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cache invalidation: explained</title>
   <link href="http://blog.knuthaugen.no//2015/01/cache-invalidation-explained"/>
   <updated>2015-01-29T00:00:00+01:00</updated>
   <id>http://blog.knuthaugen.no//2015/01/cache-invalidation-explained</id>
   <content type="html">
&lt;p style=&quot;width: 75%; margin-left: auto; margin-right: auto;font-size: 150%; font-weight: normal; font-family: times, 'times new roman', serif; font-style: italic; line-height: 130%;&quot;&gt;&quot;There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors&quot;&lt;/p&gt;

&lt;p&gt;After seeing a talk on JavaZone 2014 which touched on cache handling in a very unfulfilling way, I thought I’d take a stab at one of these supposedly hard problems. And no, it will not be off-by-one errors.&lt;/p&gt;

&lt;p&gt;There are many ways to do cache invalidation, and I am going to be talking about how we do it at Amedia, one of Norways largest online media houses. We use &lt;a href=&quot;http://www.varnish-cache.org/&quot;&gt;Varnish&lt;/a&gt; &lt;a href=&quot;#3&quot;&gt;[3]&lt;/a&gt; for all our caching needs and the implementation is kind of tightly coupled to that. But the &lt;em&gt;principles&lt;/em&gt; should be useful across other technologies. The central point is providing enough information in your cache objects, to be able to flush what you need, when you need it.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;The server setup which acts as a background for all this, is a fairly complex one. Amedia runs the digital parts of 79 (give or take) small and large newspapers in Norway. This means 151 app servers (excluding the CMS servers, which are 246 instances alone) of which 39 are running Varnish instances with differing configurations. These are physical or virtual machines running multiple apps, spread across 10 different environments and 3 data centers. This system has roughly 6.5M page views daily (in prod) and the sustained throughput of the front varnishes during the day is about 45 Mbps of traffic (each) and combined bandwith usage is around 800Mb/s in the daytime.&lt;/p&gt;

&lt;p&gt;Every piece of data, except app &amp;lt;-&amp;gt; database communication, runs over HTTP and through Varnish caches. There is caching in every step of the architecture and it follows that we need to finely tune the cache times, the cache headers and the tools to invalidate on a coarse or fine grained level.&lt;/p&gt;

&lt;p&gt;Also, the main use of cache invalidation is journalist writing and updating articles (and other content created by internal users in a similar way) and we, devopsers and developers flushing cache more or less manually by deploying apps, fixing bugs and generally sorting through all the weirdness we can experience in our stack.&lt;/p&gt;

&lt;h2 id=&quot;cache-headers-in-general&quot;&gt;Cache headers in general&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc7234&quot;&gt;RFC 7234&lt;/a&gt; &lt;a href=&quot;#1&quot;&gt;[1]&lt;/a&gt; (the revised HTTP/1.1 spec, cache portion) mentions the normal cache headers, which can be useful to know about, even though all of them are not part of the cache invalidation scheme I will be discussing.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Age&lt;/em&gt; is often set by a cache to describe how old an object is. Apps can also set it, if needed.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Expires&lt;/em&gt; sets a human readable timestamp that specifies at which time in the future (hopefully) this object is to be considered no longer fresh and fetched again. In practise it is not needed in our setup as &lt;em&gt;Age&lt;/em&gt; together with &lt;em&gt;max-age&lt;/em&gt; and &lt;em&gt;channel-maxage&lt;/em&gt; specifies what we need.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Max-age&lt;/em&gt; is how long, in seconds, should this object be cached by a browser/end user.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Channel-maxage&lt;/em&gt; is how long should a cache caches this object, in seconds (not in the RFC).&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Cache-Control&lt;/em&gt; is the collecting header where you can specify multiple caching values in one header, and groups as we shall see.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See RFC 7234 for the whole truth on these headers. When one app uses several other apps under the hood, the lowest channel-maxage and max-age header from all the backends is used for the response. So a compound response is never older than the youngest “member” object.&lt;/p&gt;

&lt;h2 id=&quot;extensions-to-cache-channel&quot;&gt;Extensions to cache-channel&lt;/h2&gt;

&lt;p&gt;A &lt;a href=&quot;http://tools.ietf.org/html/rfc7234&quot;&gt;draft by Mark Nottingham&lt;/a&gt; &lt;a href=&quot;#2&quot;&gt;[2]&lt;/a&gt; back in 2007 introduced the concept of &lt;em&gt;Cache channels&lt;/em&gt; which are specified as an extension to the cache-channel header (See RFC 7234). Extensions are a part of the HTTP/1.1 spec (revised) and nothing new, but the channel, channel-maxage and group extensions were introduced in this draft but seem to have been shelved after that. I can’t find a mention in any RFC after this. But Varnish can implement this easily through VCL and this is what we do. And it is very useful for cache invalidation.&lt;/p&gt;

&lt;h2 id=&quot;varnish-concepts&quot;&gt;Varnish Concepts&lt;/h2&gt;

&lt;p&gt;Some key varnish concepts you should be familiar with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Purge&lt;/em&gt;: A purge removed an object (and its variants) from object memory immediately.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Ban&lt;/em&gt;: A ban will add the object to a list and that list will filter objects in the cache. On the ban list means not served, and thus fetched again from the backend. The ban list is also used by the ban lurker process walking the object space and evicting objects.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Softban&lt;/em&gt;: A soft ban will ban the object but put in a state of &lt;em&gt;grace&lt;/em&gt;. This means it will only be evicted if it can be fetched again. If not, the old one will be served.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Grace mode&lt;/em&gt;: Enables grace time for objects softbanned. Grace time is of course configurable.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Saint mode&lt;/em&gt;: This mode lets you configure Varnish to not ask a backend for an object for a period of time, in case of errors or other unwanted replies. If all backends fails and saint kicks in, the existing object will be served according to grace config.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;headers&quot;&gt;Headers&lt;/h2&gt;

&lt;p&gt;Under this regime HTTP headers becomes all important and not something you just throw around for good measure. They make or break the performance of the whole stack, and need to be kept a watchful eye on. The first thing to be checked when a new app approaches production, is that the cache headers make sense and follow protocol.&lt;/p&gt;

&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;

&lt;p&gt;We have enforced very strict rules in all our apps regarding cache headers and run them through filtering or a library removing headers we do not want. This is important! Rogue expires headers can wreak havoc on a caching solution such as this. The short version is this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We do not use &lt;em&gt;expires&lt;/em&gt;. Ever. Expires is time in human readable form, while ages are in seconds. The combination can be hard to debug to say the least.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Cache-control&lt;/em&gt; is used, exclusively. &lt;em&gt;Channel-maxage&lt;/em&gt; communicates the TTL for &lt;em&gt;this&lt;/em&gt; object to varnish. When Varnish receives a request, the age of the object is compared to the channel-maxage, and this determines wether a cached copy is returned, of a new one is fetched. 
Max-age is used to set a reasonable default for browsers (Varnish does not use this) to facilitate debugging.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Cache-control groups&lt;/em&gt; are added for all necessary keywords for invalidating the cache for a multitude of scenarios, see example below.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Varnish does not out of the box use channel-maxage so this is our implementation in VCL. If channel-maxage is specified, that overrides age/expires that may or may not be present in the object.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cp&quot;&gt;# handle channel-maxage in cache-channels, override ttl 
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vcl_fetch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;control&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;channel-maxage=[0-9]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;.*channel-maxage=([0-9]+).*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3666&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;cp&quot;&gt;# subtract age if it exists
&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;cp&quot;&gt;# debug
&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CC:beresp.ttl before: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; beresp.http.age: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; beresp.ttl after: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;CC:channel-maxage found in &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;control&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, duration: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3666&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Age: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, beresp.ttl: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;beresp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ttl&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;example&quot;&gt;Example&lt;/h3&gt;

&lt;p&gt;The app &lt;em&gt;pollux&lt;/em&gt; generates complete web pages meant for the end user browser. The data comes from severals systems. One is the data backend, connected to the CMS for the relevant publication. One other is for ad information and a third is a component app, producing parts of the page (served through ESI) again calling other systems further down. These are HTTP requests done in the backend when serving up the page. All these responses have cache-control groups on them, relevant for the app serving them. These are then aggregated up the chain and gets added to the final response to Varnish. Varnish removes them on the way out to the browser, replacing them with “must-revalidate” so the browser always asks Varnish for a fresh copy. But these groups are stored with the object in Varnish and can be used to invalidate the object, on demand if the object should need to be before it expires. Allow me to illustrate:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;HTTP/1.1 200 OK
Date: Sun, 05 Oct 2014 16:18:54 GMT
Cache-Control: must-revalidate, channel-maxage&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;216, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/pub41&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/relax-isdans&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/ece_frontpage&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/sec71&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/dashboard&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/sec25292&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620213&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619956&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620986&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7498595&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620735&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619069&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619936&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620157&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619542&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7618923&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617985&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617623&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617283&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617256&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617019&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7613958&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7612903&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7615510&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7615883&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7615813&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art5520206&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622276&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622263&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622226&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622224&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622201&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622165&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622067&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621945&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621937&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621892&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621926&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621706&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621476&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7618522&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621204&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art5520202&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621332&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621874&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620605&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619046&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620962&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620562&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620557&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620036&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620014&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7436640&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621809&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619526&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622215&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622347&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7367496&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621708&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art6456542&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621547&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621542&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619791&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617979&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621393&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619986&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620883&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7622414&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621772&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619994&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7619234&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art5520191&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617945&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7618720&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7621635&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7617936&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7618688&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620739&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620879&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620237&quot;&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/art7620872&quot;&lt;/span&gt;
X-Cache-Status: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; normal &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
X-Trace-App: &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; pollux &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; Sun, 05 Oct 2014 16:18:54 GMT &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; acpcomposer &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; Sun, 05 Oct 2014 12:10:14 GMT &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; acpece4 &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; Sun, 05 Oct 2014 12:10:14 GMT &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; relax &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; Sun Oct 05 14:10:12 CEST 2014 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
Surrogate-Control: ESI/1.0
Content-Type: text/html&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;UTF-8
Transfer-Encoding: chunked&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We see the channel-maxage for this object is calculated by Varnish to be 216 seconds, and that is how long it will live in the cache if no purging occurs before that. We also include the &lt;em&gt;must-revalidate&lt;/em&gt; keyword for responses meant for browsers, so they will ask varnish on each request. Response headers meant for other apps do not include this.&lt;/p&gt;

&lt;p&gt;Here we see the &lt;em&gt;groups&lt;/em&gt; for the article data, containing the groups for the publication, the app, the article id and referenced article ids. These will ensure that this page is purged automatically if a journalist edits one of these articles. If we, for some reason want to purge every article, section and front page for that publication, we purge “group=/pub41” and we’re done. Or we purge individual articles or sections, which of course is the common case, for CMS data.&lt;/p&gt;

&lt;p&gt;One thing to be mindful of with this model is that if the &lt;em&gt;Cache-Control&lt;/em&gt; header exceeds 2048 chars in length, you will run into all sorts of funky error statuses (like 413) from the (jetty) server apps involved, and possibly Varnish too. These can be confusing and hard to debug. So we have code in place to cut groups from the header, if the header grows too long. Different server stacks can use different, more or less arbitrary limits here.&lt;/p&gt;

&lt;p&gt;As this applies to all apps, we could just as easily purge all objects using ad data, or using menu data. Or in fact almost everything in our caches in one go. Remember: with great power comes great responsibility.&lt;/p&gt;

&lt;p&gt;Here’s the Varnish VCL code to allow PURGE requests to softban&lt;a href=&quot;#5&quot;&gt;[5]&lt;/a&gt; objects from the cache.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vcl_recv&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;PURGE&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;purge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;405&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Not allowed.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;softban&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;obj.http.Cache-Control ~ group=&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;} + req.url + {&quot;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Purged.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The implementation and use of this feature is in essence the varnish-cc daemon doing curl on the varnish servers with the HTTP method set to PURGE with the name of the group we want to purge in the path.&lt;/p&gt;

&lt;p&gt;The whole chain from backend system registering that someone is editing an object, to the varnish cache being invalidated look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/arch_exp.001_s.jpg&quot; width=&quot;800&quot; height=&quot;384&quot; alt=&quot;Cache invalidation architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The app itself will send a HTTP message to Atomizer &lt;a href=&quot;#6&quot;&gt;[6]&lt;/a&gt; saying that a certain cache-control group should be invalidated. Atomizer (open sourced by us, under the Apache license) persists this in a MongoDB database. The atom feed that Atomizer produces is a 30 second rolling window of cache invalidation events, which atomizer-cc (a perl script, of all things) reads and sends PURGE requests to varnish instances. One varnish cc for each varnish instance is required in this setup. Varnish CC also holds some state internally to make sure that we don’t purge objects that just have been purged, via timestamps but it is quite simple (if you can call anything written in Perl simple, that is).&lt;/p&gt;

&lt;h3 id=&quot;some-stats&quot;&gt;Some stats&lt;/h3&gt;

&lt;p&gt;On a normal day, there are about 1500 journalists writing and updating articles, and classified ads being created feeding this system with cache invalidation requests. Normally during the day we see about 50-150 invalidation requests per minute, with an average around 60 or so.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a name=&quot;1&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc7234&quot;&gt;http://tools.ietf.org/html/rfc7234&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;2&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://tools.ietf.org/html/draft-nottingham-http-cache-channels-01&quot;&gt;http://tools.ietf.org/html/draft-nottingham-http-cache-channels-01&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;3&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.varnish-cache.org/&quot;&gt;http://www.varnish-cache.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;4&quot;&gt;&lt;/a&gt;&lt;a href=&quot;http://www.smashingmagazine.com/2014/04/23/cache-invalidation-strategies-with-varnish-cache/&quot;&gt;http://www.smashingmagazine.com/2014/04/23/cache-invalidation-strategies-with-varnish-cache/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;5&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://www.varnish-cache.org/docs/3.0/tutorial/purging.html&quot;&gt;https://www.varnish-cache.org/docs/3.0/tutorial/purging.html&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a name=&quot;6&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/amedia/atomizer&quot;&gt;https://github.com/amedia/atomizer&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>A Usable and Robust Private Npm Repo</title>
   <link href="http://blog.knuthaugen.no//2014/10/usable-and-resilient-npm-repo"/>
   <updated>2014-10-15T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2014/10/usable-and-resilient-npm-repo</id>
   <content type="html">
&lt;p&gt;Since we started using a node.js stack in Amedia we have gone through several generations of private npm repos. And since we finally have found a model that acutally works I thought I’d share it.&lt;/p&gt;

&lt;p&gt;Our main reason for having a private npm repo is to be able to deploy and work without being dependant on the global repo. We follow this model for all our app stacks (using nexus for java, private gem server for ruby). We also have internal node modules shared between applications which we won’t publish to global repo because of, ehm, reasons. All repo managers go down, and you can bet they will go down when it least fits you. Been there done that. And the long haul network latency can be a pain too.&lt;/p&gt;

&lt;h2 id=&quot;first-version-replicating-couchdb-repo&quot;&gt;First version: replicating couchdb repo&lt;/h2&gt;

&lt;p&gt;The first repo we set up was following the “standard” guide of using couchdb and the &lt;a href=&quot;https://github.com/npm/npm-registry-couchapp&quot;&gt;couchapp&lt;/a&gt; that runs the global repo. And what a total disaster it was. The replication was growing in size more or less by the minute by node.js hackers reinventing every wheel they can lay there eyes on. Granted, starting out with too small disks was my bad, but worse was also that couch was unstable and the replication thread was spontanously dying on us and not tell a soul about it. Suddenly the repo was out ouf date or just down.  Not much better than using the global repo, actually worse at times. Granted, this was on an earlier version of couchdb, but it caused a lot of pain.&lt;/p&gt;

&lt;h2 id=&quot;second-version-reggie&quot;&gt;Second version: reggie&lt;/h2&gt;

&lt;p&gt;Then we found &lt;a href=&quot;https://github.com/mbrevoort/node-reggie&quot;&gt;reggie&lt;/a&gt; as a lighweight, filebased alternative. It used a separate client for publishing and version specs in package.json used a different syntax than normal, but it was stable and usable. Reggie would only hold our internal packages so we returned to using the global repo for public packages. But then npm inc. happened and with that a lot of changes came into npm. Reggie was abandoned by its authors and soon became incompatible with newer node/npm versions. So what to do now?&lt;/p&gt;

&lt;h2 id=&quot;threes-a-charm&quot;&gt;Three’s a charm&lt;/h2&gt;

&lt;p&gt;After reading about the setup at &lt;a href=&quot;http://www.finn.no/&quot;&gt;Finn&lt;/a&gt; but short on details I set out to stitch something together. By this I had testet using &lt;a href=&quot;https://github.com/mixu/npm_lazy&quot;&gt;npm_lazy&lt;/a&gt; as a caching proxy for registry.npmjs.org, to avoid downtime and latency, and that worked well. To work around the (major) deficiency in npm, with only support for one repo, I used &lt;a href=&quot;https://github.com/krakenjs/kappa&quot;&gt;Kappa&lt;/a&gt; as the repo software. Kappe pointed to two backends, a npm repo couchapp running in a recent couchdb as the first (for writing and holding our internal packages) and npm_lazy as the second, acting as a caching proxy between us and registry.npmjs.org. A bonus is that we now use the exact same syntax as global packages and use npm for publishing.&lt;/p&gt;

&lt;p&gt;And since this baby came into production, I can’t recall a single error or minute of downtime for it.&lt;/p&gt;

&lt;p&gt;That makes me a happy camper.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Metrics Approach</title>
   <link href="http://blog.knuthaugen.no//2013/10/the-metrics-initiative"/>
   <updated>2013-10-20T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2013/10/the-metrics-initiative</id>
   <content type="html">
&lt;p&gt;Recently we started a new initiative at work, and as per usual in our company, things just start to trickle into production when somebody (usually I) get an idea. This time, it was metrics. I read some articles from Etsy and not to forget Coda Hale from Yammer (at the time, at least) (see &lt;a href=&quot;http://pivotallabs.com/139-metrics-metrics-everywhere/&quot;&gt;http://pivotallabs.com/139-metrics-metrics-everywhere/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The itch started with wanting to know more about what our apps are doing, what effekt our deployment has (at least when arrow start to point in the wrong direction) and generally just know more. Our stack is roughly divided in three non-equal parts: java, ruby and node.js and I wanted something that could handle metrics from all 3 easily.&lt;/p&gt;

&lt;p&gt;As the metrics backend we chose &lt;a href=&quot;http://graphite.wikidot.com/&quot;&gt;graphite&lt;/a&gt;, written in python, which is fairly easy to set up, stable and performant for a large number of reporting apps. It supports sending data over both TCP and/or UDP and uses a simple, debuggable plaintext protocol format.&lt;/p&gt;

&lt;h2 id=&quot;getting-your-data-into-graphite&quot;&gt;Getting your data into graphite&lt;/h2&gt;

&lt;p&gt;This is the easy part. You can send pretty much from anything. Command line netcat stuff, java libraries, ruby gems and node modules make sending metrics quite easy. Once they are in graphite, the harder work of figuring out what and how to display the information begins. Metrics not shown, are pretty much useless. Graphites web graphing capabilities are pretty good, but I ended up writing a small in-browser-app wrapping it and making it even easier to navigate graphs, create simple dashboards and set up new graphs when they appear. I still use the native graphite system for testing out new metrics and experimenting. And support for SVG graphs is pretty cool. Fullscreen graphs in a heartbeat.&lt;/p&gt;

&lt;p&gt;For java apps, we chose &lt;a href=&quot;http://metrics.codahale.com/&quot;&gt;metrics&lt;/a&gt; from none other than Coda Hale, which is a well written java library for reporting arbitraty metrics from any app and storing them in graphite (among other backend). Easy integration into an already shared lib made it very easy to report from multiple apps in a couple of days.&lt;/p&gt;

&lt;p&gt;For now, we measure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Exception rates&lt;/li&gt;
  &lt;li&gt;HTTP status rates for apps concerned with serving content&lt;/li&gt;
  &lt;li&gt;failed/sucessfull logins for user apps&lt;/li&gt;
  &lt;li&gt;Load time, render time, request times etc. for the front pages of our newspapers (via a private instance of &lt;a href=&quot;http://www.webpagetest.org&quot;&gt;webpagetest.org&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Production deploys for apps&lt;/li&gt;
  &lt;li&gt;Sonar violations and duplications for java apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a key part here is that we graph both e.g. exceptions for an app with deployments for that app and relevant collaborating apps. This has already saved our bacon several times, when we see the graph go up just after a deploy, and dive in to fix it straight away. Especially for the front end performance graphs this is very nice (we really don’t want to increase our load time any more than necessary).&lt;/p&gt;

&lt;p&gt;Here’s an example graph showing deploys (vertical yellow and brown lines) causing a spike in render times for our front page:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/render.svg&quot; width=&quot;651&quot; height=&quot;445&quot; alt=&quot;Render time graph, with deploys&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;display&quot;&gt;Display&lt;/h2&gt;

&lt;p&gt;Metrics need to be shown to be understood. We have several big screens around the office showing some of the most interesting graphs and this has led to discovering bad deploys pretty quickly. But we already see that there are too few screens to show everything we should show.&lt;/p&gt;

&lt;p&gt;An interesting project I aim to take a look at is the &lt;a href=&quot;http://codeascraft.com/2013/06/11/introducing-kale/&quot;&gt;Kale stack&lt;/a&gt; consisting of Skyline and Oculus (cool names!). This is a anomaly detection system (Skyline) and anomaly correlation system (Oculus) written specifically for handling a large number of metrics - large enough that you can’t watch them all. Etsy have a lot of cool stuff - and not just the merchandise.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;We do not, as of yet, report any metrics from ruby or node apps, but we definitely plan to, using the relevant libraries in Ruby and Node. We mostly display technical metrics for the time being and the reasons for this are: We have other system reporting stuff like click-through tracking, user time, sessions behaviour etc. One idea is to incorporate this into the same graphs as, for instance, deployments to see if there is a pattern to user behaviour after deployments of new feature.&lt;/p&gt;

&lt;p&gt;More technical metrics regarding A/B testing is also something we should look at in the near future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rapid Java Deployment</title>
   <link href="http://blog.knuthaugen.no//2012/11/rapid-java-deployment"/>
   <updated>2012-11-15T00:00:00+01:00</updated>
   <id>http://blog.knuthaugen.no//2012/11/rapid-java-deployment</id>
   <content type="html">
&lt;p&gt;At my job, Norway’s now second largest online (and paper) newspaper company, I have been doing some work to cut down on deployment and delivery times for our java applications, as well as simplifying the procedures involved. Continuous Delivery is a hot buzzword nowadays, along with DevOps and I think we are quite good at the latter, but we needed improvement on the former. Besides, continuous delivery is about continuous improvements to the pipeline too. And we had some stales processes and tools in place.&lt;/p&gt;

&lt;h2 id=&quot;background-and-setup&quot;&gt;Background and setup&lt;/h2&gt;

&lt;p&gt;We have 4 different environments to which we deploy our software, snapshot, stage, test and production, and snapshot has 4 versions so each team can use one for their stuff, without being overwritten by other teams. Local developer setups exists also, but are not covered here. These are proper, rather beefy servers with quite a lot of apps running, plus two varnish instances.&lt;/p&gt;

&lt;p&gt;We use puppet to ensure how the servers are set up, and to distribute changes to config and setup. Puppet is mainly the concern of the ops team.&lt;/p&gt;

&lt;p&gt;The old script for deploying one app to, say, snapshot1, was a shell script that did the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Run a script on the ci server (CruiseControl) and copy all latest artifacts for all relevant apps (8-10 java/jetty apps of varying size)&lt;/li&gt;
  &lt;li&gt;Commit these in a subversion repository&lt;/li&gt;
  &lt;li&gt;Do some magic tagging in subversion&lt;/li&gt;
  &lt;li&gt;Run puppet-sync on the relevant servers, reading binary data from subversion.&lt;/li&gt;
  &lt;li&gt;Restart varnish caches for the environment in question.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Time spent: around 10 minutes, not counting the varnish warm-up on the first request. Also you &lt;em&gt;had&lt;/em&gt; to wait for CruiseControl to build the app before it could be pushed. There could be a queue.&lt;/p&gt;

&lt;p&gt;And most of the time, you were only deploying one or maybe two apps, but everything was done nevertheless. This was needed every time you wanted to deploy an app for someone else to see, a product owner for instance. The reason this had become so slow, was, as is the case in many organizations, that it is written for a simpler time, but then at one point, it stopped evolving along with the organisation and the app ecosystem. This is a grave mistake.. The number of apps to support grows, the puppet-syncing from subversion gets slower, the varnish refresh takes longer etc. It all adds up. And puppet isn’t very fast, and syncing large binary files is something it is particularly bad at.&lt;/p&gt;

&lt;p&gt;CruiseControl was also slow at building several apps at once, and you often got some coffee time there too.&lt;/p&gt;

&lt;p&gt;What was worse, was that when deploying to stage, test and prod, there was no shared script. There were a few lying around, but nothing solid. And you had to do parts of it manually. Run shell commands for tagging, finding the right servers (they change) and puppet-syncing. And for instance for production, you can’t do all 6 servers at once. You take one or two, and wait for the varnish ping to find them, let it warm up, and do the next - all manually and watching as servers came up and down. Manual procedures are a pain, and many developers were vary of this. Lots of help seeking and asking of questions. And mind you, this procedure is not done by sysadmins, but by the developers. This is a good thing, but the threshold for doing it, and doing it well, had to be lowered. Our goal is that all developers should be able to push to production, safely, fast and with confidence.&lt;/p&gt;

&lt;p&gt;The rest of the blog post is about what we did to improve this.&lt;/p&gt;

&lt;h2 id=&quot;the-new-way&quot;&gt;The New Way&lt;/h2&gt;

&lt;p&gt;Goals were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Faster CI pushing artifacts to repo manager.&lt;/li&gt;
  &lt;li&gt;Scripted one-click for all environments&lt;/li&gt;
  &lt;li&gt;Not do more than was required, i.e. only deploy and restart the apps specified.&lt;/li&gt;
  &lt;li&gt;Ditch puppet for syncing large binaries, use it for server config only.&lt;/li&gt;
  &lt;li&gt;Speed things up in general.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a backdrop, we migrated to Jenkins with four build nodes for speeding up build times when there is a lot going on. Simultaneously, we migrated from subversion to git, and started using Java7 and maven3 (yes, I know, we’re slow on the uptake).&lt;/p&gt;

&lt;p&gt;The main job here was to create a script that was to be run on the app servers themselves which would do:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get a app name and a version on the command line&lt;/li&gt;
  &lt;li&gt;Fetch that app from a Nexus repo, other file repo or git repo&lt;/li&gt;
  &lt;li&gt;Optionally restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several benefits to this. One is that it is easy to script, another that it is very flexible and you can choose to run it via puppet, manually or via a script.&lt;/p&gt;

&lt;p&gt;So we then made some big changes. All apps got snapshot builds (by maven) deployed to nexus, alongside release builds. Snapshots are pushed from jenkins CI, or manually. But we also added the option of building an app from source if you need to e.g. push a branch to a server for someone to test. The old deployment routine only pushed war files for the webapp project to the server, while now we build a zip file bundling the war file and the dependencies needed for jetty startup too. This will make future jetty upgrades much easier too.&lt;/p&gt;

&lt;p&gt;The deploy scripts looks like this on the command line:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;update-dev &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; dev0 appname
&lt;span class=&quot;c&quot;&gt;# will update appname to the latest snapshot release on environment dev0&lt;/span&gt;

update-dev &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; dev0 appname
&lt;span class=&quot;c&quot;&gt;# will update appname to the latest release version on environment dev0&lt;/span&gt;

update-dev &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; dev0 
&lt;span class=&quot;c&quot;&gt;# will update all apps to the latest release version on environment dev0&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# if you want a specific version, either release or snapshot, do&lt;/span&gt;
update-dev dev0 appname:1.45

&lt;span class=&quot;c&quot;&gt;# if you want to custom-build either do&lt;/span&gt;

update-dev dev0 appname:branch-foo:21451a
&lt;span class=&quot;c&quot;&gt;# to clone repo for appname, and build revision 21451a of branch &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# branch-name. This is then copied to git repo, and deployment &lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# script fetches it from there. if revision is omitted, you get HEAD (no pun intended).&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The main points here are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Only update the apps you specify via the script running on the server.&lt;/li&gt;
  &lt;li&gt;Optionally update server config, via puppet.&lt;/li&gt;
  &lt;li&gt;Ban URLs in Varnish, don’t restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to this, the script asks puppet which server is the current one for the environment, instead of hard coding this in the script. This means less maintenance burden when servers change. A central point is also to only restart apps you specify (instead of all apps) and flushing/banning Cache-Control groups in Varnish instead of restarting.&lt;/p&gt;

&lt;p&gt;The work is distributed between the two scripts thusly:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Script run on dev machine checks for valid version&lt;/li&gt;
  &lt;li&gt;Does custom build if needed&lt;/li&gt;
  &lt;li&gt;Finds servers&lt;/li&gt;
  &lt;li&gt;Other misc. stuff like command line parsing&lt;/li&gt;
  &lt;li&gt;Calls the right script on the right server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Script on server will then&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fetch version&lt;/li&gt;
  &lt;li&gt;Unzip app bundle, move stuff into place&lt;/li&gt;
  &lt;li&gt;Tell puppet which version is now current for the environment in question&lt;/li&gt;
  &lt;li&gt;Optionally restart&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then dev script again takes over and flushes caches.&lt;/p&gt;

&lt;p&gt;Updating one app to latest snapshot or release and banning app URLs in Varnish is now down to 10-12 seconds not counting savings in cache fill time for the first request. Several of these seconds are spent querying puppet for metadata. This will be a bit faster in production.&lt;/p&gt;

&lt;h2 id=&quot;further-work&quot;&gt;Further Work&lt;/h2&gt;

&lt;p&gt;Next up is using the same deployment strategy for all environments, but with a custom rollback feature for production and a wait strategy between servers. We thinking about extending this to not wait a certain amount of time, but rather monitor Varnish and move on once the backend is reported as up, and answering requests. Also, a wrapper script to be called from Jenkins for deploying to stage, run smoke tests, await result and deploy to prod if all is well is planned. And of course, no varnish flushing in production.&lt;/p&gt;

&lt;p&gt;All in all, shaving of ~10 minutes of deploy time (this amounts to hours of wait/ineffective time on a busy day) is pretty significant. But the main win is less manual procedures in production, more power to the developers and faster delivery, when we need to.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Helpful stack traces? Not so Much</title>
   <link href="http://blog.knuthaugen.no//2012/10/helpful-stacktraces-not-so-much"/>
   <updated>2012-10-10T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2012/10/helpful-stacktraces-not-so-much</id>
   <content type="html">
&lt;p&gt;I am dabbling around with &lt;a href=&quot;http://nodejs.org&quot;&gt;Node.js&lt;/a&gt; at work these days and we are trying out &lt;a href=&quot;https://github.com/mde/jake&quot;&gt;Jake&lt;/a&gt; as a build script. Jake itself is not so bad and the syntax is pretty neat, with a very small DSL for writing build scripts in JavaScript. But, with some google-fu, it’s not that hard to find accounts of people not knowing what to make of a node stack trace. So, here’s what I got when running a build script:&lt;/p&gt;

&lt;style type=&quot;text/css&quot;&gt;pre code { font-size: 90% !important; }&lt;/style&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;nikopol: &lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;jake &lt;span class=&quot;nt&quot;&gt;--trace&lt;/span&gt; build-extension
jake aborted.
Error: Process exited with error.
    at fail &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;~/nvm/v0.8.2/lib/node_modules/jake/lib/api.js:221:18&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    at utils.mixin.exec &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;~/nvm/v0.8.2/lib/node_modules/jake/lib/utils/index.js:64:9&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    at EventEmitter.emit &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;events.js:91:17&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    at ChildProcess.utils.mixin._run &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;~/nvm/v0.8.2/lib/node_modules/jake/lib/utils/index.js:186:20&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    at ChildProcess.EventEmitter.emit &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;events.js:91:17&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    at Process._handle.onexit &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;child_process.js:674:10&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hookay, something is clearly wrong here but what is it? There’s some event emitting going on, and some mixins being visited. And an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec()&lt;/code&gt; there too. Well, if we inspect the build script there is exec’ing of some shell commands for catting some files together. But what is the error being thrown from the shell command? Turns out, the error is actually a missing directory. Jake and Node could really be more helpful by giving some hints of that. No mention of the underlying OS error, no mention of the exit status, nor the command throwing the error (the exec call can take an array of commands to exec) or anything. Score -1 for Jake when it comes to good error messages. I’ll see if I’ll try to fork it and submit a patch on the mixin._run method to spare some other soul some useless debugging.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Tweet Storm</title>
   <link href="http://blog.knuthaugen.no//2012/10/tweet-storm"/>
   <updated>2012-10-08T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2012/10/tweet-storm</id>
   <content type="html">
&lt;p&gt;A while back I experienced the full force of Twitter, in a nutshell. I am not a very active twitter user, but I follow som celebrity developers and once in a while I throw out a tweet in reply to one. This normally just leads to (at best) a few tweets back and forth and nothing more. But not this time. I responded to a discussion between &lt;a href=&quot;https://twitter.com/tedneward/&quot;&gt;Ted Neward&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/shanselman&quot;&gt;Scott Hanselman&lt;/a&gt;, both with a sizable number of followers. They were talking about some presentation from Microsoft on best practises. I spoke my mind on it:&lt;/p&gt;

&lt;p style=&quot;width: 75%; margin-left: auto; margin-right: auto;font-size: 150%; font-weight: normal; font-family: times, 'times new roman', serif; font-style: italic; line-height: 130%;&quot;&gt;&quot;There are no best practices, there are only practices and their suitability depends on the context.&quot;&lt;/p&gt;

&lt;p&gt;This lead to a retweet from first Ted Neward and then Scott Hanselman. And then things really took off. In the next 24 hours I got something like 50-60 retweets, just from being retweeted by some celebrities. Granted it’s not &lt;em&gt;that&lt;/em&gt; many, but so much more than I have ever gotten before. That’s simply how Twitter work. Funny thing is that only one (1) spoke out in disagreement of the statement.&lt;/p&gt;

&lt;h3 id=&quot;context-is-king&quot;&gt;Context is King&lt;/h3&gt;
&lt;p&gt;The statement in itself is a post modern stance on applying practises in software development. Some, but not that many, argue that some practises are good regardless of the context you are applying them in. I disagree. In the past I have felt this too, but every single time someone or something has some time later disproved that, and I now try to think about the context before doing anything regarding process or practises. Universal truths are few and far between, especially in software development.&lt;/p&gt;

&lt;p&gt;For instance, I have been, and still am, when the context fits, a strong proponent of some practises:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Writing tests first, and writing tests in general.&lt;/li&gt;
  &lt;li&gt;Don’t copy and paste code and keep duplication low.&lt;/li&gt;
  &lt;li&gt;Monitoring code quality (test coverage, coupling, cyclomatic complexity etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I have then seen talks from e.g. &lt;a href=&quot;https://twitter.com/tastapod&quot;&gt;Dan North&lt;/a&gt; about the work at &lt;a href=&quot;http://drw.com&quot;&gt;DRW&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/fgeorge52&quot;&gt;Fred George&lt;/a&gt; about the work at &lt;a href=&quot;http://forwardtechnology.co.uk/videos/3244732&quot;&gt;Forward&lt;/a&gt;. For instance at Forward they have a micro service architecture, where a larger system is built of tens or hundreds of really small services or apps. Really, really small. So what happens to your feelings towards unit testing when the entire app is maybe 100-200 lines of code? And the maintainability of the code? Is it more efficient to replace (write a new one) that change it, if the change is more than a small fix? This is actually what they do at Forward: write little to no tests and replace apps rather than change them (for bigger values of “change”). Continuous integration? Don’t need it. Refactoring? Nope. Sprints? Nix. User stories? Nope. Developers and Customers talking to each other making the decisions. This is the single responsibility principle applied to the application level, not the class or module level.&lt;/p&gt;

&lt;p&gt;Dan North has talked about how his attitude towards classic agile methods when he started at DRW. He has collected some new patterns (of effective software delivery, as he calls them) of how experienced programmers can deliver software much faster than the so-called “hyper productive teams” that Scrum is peddling. And this is done by breaking the rules of classic agile and using other practices instead. is reasoning is that class agile presupposes a lot of things, and when those things aren’t true, neither is the practise that it mandates. And so other practises emerge. Like using TDD as an exploration tool, starting with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;private void testBlah()&lt;/code&gt; and just exploring the code with no intent of keeping the test. But &lt;em&gt;if&lt;/em&gt; you decide to keep the test, clean it up, change the name and commit it. Likewise with a class. Just start with a class, mess around with it and later on decide if you want to keep it or not. Defer the commitment to the code and write tests for it later, and make it all TDD-shaped and nice. But not before you know you are going to keep it.&lt;/p&gt;

&lt;p&gt;These are really interesting thoughts and shows that some practises, that seem very sane in some (or perhaps most) contexts, will disappear completely in another. And both DRW and Forward are hugely successful at what they do, too. Kent Beck has been touching on this in his talks on &lt;a href=&quot;http://video.javazone.no/talk/28803277&quot;&gt;The Effects of Acceleration&lt;/a&gt; where he talks about the effects of faster delivery has on your practices. Well worth checking out.&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://vimeo.com/43659070&quot;&gt;Dan North on Patterns of Effective Delivery&lt;/a&gt; and &lt;a href=&quot;https://vimeo.com/24681032&quot;&gt;another incarnation of the same talk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://forwardtechnology.co.uk/videos/32447325&quot;&gt;Fred George on Programmer Anarchy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>JavaZone Video Mashup</title>
   <link href="http://blog.knuthaugen.no//2012/10/javazone-video-mashup"/>
   <updated>2012-10-02T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2012/10/javazone-video-mashup</id>
   <content type="html">
&lt;p&gt;&lt;a href=&quot;http://video.javazone.no&quot;&gt;Video.javazone.no&lt;/a&gt; was just sorta soft launched, a mashup site collecting the videos of talks from &lt;a href=&quot;http://www.javazone.no&quot;&gt;JavaZone&lt;/a&gt; from 2009 through 2012. As with any project where you don’t have the time you want, there are some things I am happy with, and some things i am, well not so happy about. But overall I am pretty happy.&lt;/p&gt;

&lt;p&gt;The site collects a total of 417 talks from 290 speakers over the last four years, and 100 additional talks from the same speakers from other conferences and meetups. The main features are more metadata on the talks, display of session information (abstract, speaker bio etc.) alongside the vimeo video and metadata about the video.&lt;/p&gt;

&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;

&lt;p&gt;The main app is written in &lt;a href=&quot;http://playframework.org&quot;&gt;Play Framework&lt;/a&gt; 1.2.5 and using &lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt; in the backend. The reason for choosing MongoDB is primarily that it is easy to work with (low developer friction) and has enough querying capabilities for this project. And it’s not SQL. The data model also fits pretty well, with little coupling of different objects at the database level. A simple db reference handles it nicely. MongoDB has some, how should I put it, &lt;em&gt;interesting&lt;/em&gt; views on data integrity and there are situations where you can lose data running on a single node. The data set for this app is all generated and updated while running, but can be recreated at any time. So losing data isn’t a big deal, and it doesn’t happen &lt;em&gt;that&lt;/em&gt; often, either. So I decided MongoDB was a nice fit.&lt;/p&gt;

&lt;p&gt;The reason for Play was mainly to test it out and see if it’s a good framework or not, and also to get away from some of the friction of using standard java server frameworks like Spring MVC (or indeed any server toolkit doing it the normal java way, which is tedious and needlessly complicated). Play lends itself quite heavily on the convention over configuration approach and removes a lot of clutter when setting up a simple controller that has responsbility over an URL, fetches some data, feeds that into a template and renders it. The fact that you default can’t run a single test but have to run all tests in a test class is a real PITA. Play has some other mild annoyances too, but generally I am pretty happy with it.&lt;/p&gt;

&lt;p&gt;All in all, these things have been involved in the site:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://playframework.org&quot;&gt;Play Framework&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; for templating/design/responsive stuff.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://fortawesome.github.com/Font-Awesome/&quot;&gt;FontAwesome&lt;/a&gt; for nice icons.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://sass-lang.com/&quot;&gt;SASS&lt;/a&gt; for better stylesheet authoring.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt; and git for vcs, packaging and deployment.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://heroku.com/&quot;&gt;Heroku&lt;/a&gt; and &lt;a href=&quot;https://www.mongohq.com/&quot;&gt;MongoHQ&lt;/a&gt; cloud providers for running test sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a little screenshot for you:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://video.javazone.no&quot;&gt;&lt;img src=&quot;/images/videosite-screenshot.png&quot; alt=&quot;Video site screenshot&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I collaborated with &lt;a href=&quot;https://twitter.com/bjartek&quot;&gt;bjartek&lt;/a&gt; on it for a while and as he is a scala/functional programming buff, we tried out a few different techniques. Some using &lt;a href=&quot;http://code.google.com/p/guava-libraries/&quot;&gt;Google Guava&lt;/a&gt; and some using &lt;a href=&quot;http://functionaljava.org/&quot;&gt;Functional Java&lt;/a&gt;. This made the codebase a little messy in some parts. The java syntax doesn’t really support it well, either. Will try to clean up that soon. Anyway, if you try the site, I’d love some feedback on it. Just bear in mind that I am no designer :-)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Headless Tests with Buster.js and Phantom.js</title>
   <link href="http://blog.knuthaugen.no//2012/09/headless-tests-with-buster-and-phantom"/>
   <updated>2012-09-28T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2012/09/headless-tests-with-buster-and-phantom</id>
   <content type="html">
&lt;p&gt;This setup was pieced together via several gists, tweets and articles so I thought it would be good thing to collect it all in a post. &lt;a href=&quot;http://busterjs.org/docs/browser-testing/&quot;&gt;http://busterjs.org/docs/browser-testing/&lt;/a&gt; says phantom.js support is “not yet landed in the beta”, but that is the config sugar for doing it through the buster.js config file. It works just fine nonetheless.&lt;/p&gt;

&lt;p&gt;We have been moving tests for the frontend projects at work to &lt;a href=&quot;http://busterjs.org&quot;&gt;buster.js&lt;/a&gt;  instead of &lt;a href=&quot;https://code.google.com/p/js-test-driver/&quot;&gt;JsTestDriver&lt;/a&gt;. Mainly because Buster.js is faster and has a richer set of tools built in. JsTestDriver has some annoying bugs too and does not seem to be actively developed these days.&lt;/p&gt;

&lt;p&gt;But, running JavaScript tests in a browser is fairly easy, both with Buster and JsTestDriver by capturing a browser and sending data back and forth. But when running fast unit tests and running tests in a CI server, headless tests are nice. Fast and with no need for setting up multiple browsers on the ci server running on a X display of some kind. Of course, this &lt;em&gt;can&lt;/em&gt; be done, but it is a hassle. Now if you &lt;em&gt;need&lt;/em&gt; browser testing in several browsers, I suggest using an external service for that. Anyway, for our tests, most of them are not threading the fine line between browser incompatibilities as we are developing a chrome browser extension. So we can run the tests in phantom.js normally and run them in the target browser periodically.&lt;/p&gt;

&lt;h3 id=&quot;enter-phantomjs&quot;&gt;Enter Phantom.js&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://phantomjs.org/&quot;&gt;Phantom.js&lt;/a&gt; is a 100% headless (as of version 1.6) headless webkit browser engine. It can be used for all kinds of nifty things like taking screenshots of web pages (the whole page, and not just the viewport), scripting interaction with webpages (there’s an example of using Google Maps to find driving directions) and finding the load time of a web page, to name a few cool things.&lt;/p&gt;

&lt;h3 id=&quot;installing-busterjs-and-phantomjs&quot;&gt;Installing Buster.js and Phantom.js&lt;/h3&gt;

&lt;p&gt;Install Phantom.js by downloading it and unpacking the tar-file. If you’re on a mac you can do:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;brew update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;phantomjs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Buster.js is a node module and can be installed by running the following command in a recent node version:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; buster&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;or checking out &lt;a href=&quot;http://busterjs.org/docs/getting-started/&quot;&gt;http://busterjs.org/docs/getting-started/&lt;/a&gt; if you need more handholding. Depending on your platform you may need to adjust your path variable or symlink the buster executables into your $PATH&lt;/p&gt;

&lt;h3 id=&quot;running-the-tests&quot;&gt;Running the Tests&lt;/h3&gt;

&lt;p&gt;So to run some buster tests in phantom I have made a small project with some tests&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;buster.js
bin/
  server.sh
  kill-server.sh
  phantom.js
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;/
  math-test.js
lib/
  math.js
 &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/math.js&lt;/code&gt; file is a small object with a simple function, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/math-test.js&lt;/code&gt; is a test case for that. Nothing fancy there, but I list them for completeness. These files should hold your production code and tests.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// lib/math.js&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;myapp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prototype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;square&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// test/math-tests.js&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;buster&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Make some functions global&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;A math module&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;squares 1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
         
  &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;it raises any number to its power&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
           
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The contents of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buster.js&lt;/code&gt; (buster config file):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;My tests&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;rootPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// or &quot;node&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lib/*.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;test/*.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server.sh&lt;/code&gt; is a script for starting the buster server and the phantom.js instance, and run the
phantom.js script on startup:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

buster-server &amp;amp; &lt;span class=&quot;c&quot;&gt;# fork to a subshell&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;2 &lt;span class=&quot;c&quot;&gt;# takes a while for buster server to start&lt;/span&gt;
phantomjs ./bin/phantom.js &amp;amp;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then we have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phantom.js&lt;/code&gt; script for capturing the browser, in phantom. We also redirect any alerts
to console.log() instead, since we won’t see them.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;var system &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; require&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'system'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;,
    captureUrl &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'http://localhost:1111/capture'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;system.args.length&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    captureUrl &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; system.args[1]&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

phantom.silent &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

var page &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; new WebPage&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

page.open&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;captureUrl, &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt;phantom.silent&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    //console.log&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;status &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'success'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      console.log&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'phantomjs failed to connect'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      phantom.exit&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    page.onConsoleMessage &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;msg, line, &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      var fileName &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; id.split&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      // format the output message with filename, line number and message
      // weird gotcha: phantom only uses the first console.log argument it gets :&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      console.log&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;fileName[fileName.length-1]+&lt;span class=&quot;s1&quot;&gt;', '&lt;/span&gt;+ line +&lt;span class=&quot;s1&quot;&gt;': '&lt;/span&gt;+ msg&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    page.onAlert &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      console.log&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The last script, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kill-server.sh&lt;/code&gt; is for running after the tests and shut everything down again.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# just call with ./kill-server buster-server|phantom&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;get_buster_server_pid&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;ps aux|grep buster-server|grep node|awk &lt;span class=&quot;s1&quot;&gt;'{ print $2 }'&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;get_phantom_server_pid&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;ps aux|grep phantomjs|head &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;|awk &lt;span class=&quot;s1&quot;&gt;'{ print $2 }'&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;buster-server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;server_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;get_buster_server_pid&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;phantom&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;server_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;get_phantom_server_pid&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$server_pid&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$server_pid&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; killed&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; not killed. Found pid was=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$server_pid&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Running all these together, after another, we start up a buster server on localhost:1111, start phantomjs and use that to capture the buster server, run the tests, and the kill the server and phantom afterwards.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;nikopol:buster: -&amp;gt;./bin/server.sh 
buster-server running on http://localhost:1111
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;nikopol:buster: -&amp;gt;buster-test 
PhantomJS 1.6.1, OS X: ..                                                                               
1 &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt;, 2 tests, 3 assertions, 0 failures, 0 errors, 0 timeouts
Finished &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;0.006s
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;nikopol:buster: -&amp;gt;./bin/kill-server buster-server &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./bin/kill-server phantom
buster-server killed
phantom killed
&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;nikopol:buster: -&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you’re running the tests locally, you can leave the server and phantom running. But we want to run them in the CI server, so we clean up after tests are run.&lt;/p&gt;

&lt;h3 id=&quot;running-on-jenkins&quot;&gt;Running on Jenkins&lt;/h3&gt;

&lt;p&gt;We run the build of this project in Jenkins and want to display the test results there, along with csslint, jslint and other quality checks. Buster.js has a nifty command line switch for outputting junit compatible xml, which Jenkins reads out of the box. For instance this command, instead of plain buster-test in Jenkins, with config for the violations plugin pointing to the file, will graph the test results nicely.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;buster-test &lt;span class=&quot;nt&quot;&gt;--reporter&lt;/span&gt; xml &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; reports/test-report.xml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The scripts can also of course be configured to run through npm, if you want.&lt;/p&gt;

&lt;p&gt;That’s it, happy phantom.js and buster.js hacking!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Blog Reboot</title>
   <link href="http://blog.knuthaugen.no//2012/09/blog-reboot"/>
   <updated>2012-09-26T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2012/09/blog-reboot</id>
   <content type="html">
&lt;p&gt;Firstly, I apologize for resetting the RSS feed for my blog, but that was I am afraid, unavoidable :-)&lt;/p&gt;

&lt;p&gt;I got really tired of (not) upgrading my Movable Type installation up to the point where it was becoming a blatant security risk, more or less. So, after working a lot with git lately, both at work migrating every project from Subversion over to git, and privately, I decided to switch the whole blog over to git and github. I am now using &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; and more precisely &lt;a href=&quot;http://jekyllbootstrap.com/&quot;&gt;Jekyll Bootstrap&lt;/a&gt; which is a layer built on top of Jekyll, with some themes using &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Twitter Bootstrap&lt;/a&gt; for the design stuff. A small rakefile is used for deploying the generated site to my web hosting provider.&lt;/p&gt;

&lt;p&gt;Jekyll is very easy to work with and generally I am pretty happy with it. The Hooligan theme is a bit heavy on the effects and I might tone it down when I get round to it. There are at present no comments on the blog, and I am quite frankly unsure if I need them. Perhaps some social media sharing buttons will work just as well. Anyhow, jekyll bootstrap has among others Disqus support built in, so I might take that for a spin later.&lt;/p&gt;

&lt;p&gt;Lastly, now that mountain biking season is very much near the end, I might even find time to write a few more blog posts too. Updates has been slow over the summer. I have some interesting projects going on at work now, and there might be a blog post or three later this fall. Topics will be git, continuous delivery and perhaps some NoSQL/architecture stuff. We’ll see.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>What I Create</title>
   <link href="http://blog.knuthaugen.no//2012/02/what-i-create"/>
   <updated>2012-02-24T22:43:13+01:00</updated>
   <id>http://blog.knuthaugen.no//2012/02/what-i-create</id>
   <content type="html">&lt;p&gt;Along with a lot of other people I know and follow on Twitter one of my resolutions for this year (I’m normally not big on new years resolutions) was to consume less, twitter, blogs, articles and videos, and create more. I spend far too much time on twitter, following interesting links, and well, not so interesting but &lt;em&gt;entertaining&lt;/em&gt; links and soon enough I am laughing at a cat with glasses farting unicorns off somewhere in the darkest corners of the Interntet. Enough of that!&lt;/p&gt;

&lt;p&gt;I came across a nice quote from Why the lucky stiff in Giles Bowketts &lt;a href=&quot;http://gilesbowkett.blogspot.com/2012/02/rails-went-off-rails-why-im-rebuilding.html&quot;&gt;excellent blog article from a few days ago&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;- When you don’t create things, you become defined by your tastes rather than ability. Your tastes only narrow &amp;amp; exclude people. So create.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So in order to up my game a bit, I thought I’d blog about what I create and what I intend to create in 2012.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;An ebook for my son&lt;/p&gt;

    &lt;p&gt;I described it in more detail in &lt;a href=&quot;http://blog.knuthaugen.no/2012/01/getting-my-kid-to-read-with-html5.html&quot;&gt;a blog post of it’s own&lt;/a&gt;. I am proud of this one, not so much because of the technical merit, but how well it worked. My son is reading like a champ, got praise from his teacher and is moving on the books and Donald Duck magazines. This is also an ongoing effort, as we’re only at book 3 now.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Conference tutorial&lt;/p&gt;

    &lt;p&gt;I have submitted a tutorial suggestion for the &lt;a href=&quot;http://rootsconf.no/&quot;&gt;2012 Roots conference&lt;/a&gt; on the topic of smoke testing web apps. As of writing the jury is still out on this one, but I hope it gets approved. I have done some work on smoke testing at work these last few weeks and it has already saved our bacon several times.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Javazone video site&lt;/p&gt;

    &lt;p&gt;I am also working on a video site collecting the vimeo videos of the last two years worth of presentation videos from &lt;a href=&quot;http://jz12.java.no/&quot;&gt;JavaZone&lt;/a&gt; and mashing it up with some other data sources. It’s not &lt;em&gt;quite&lt;/em&gt; ready for prime time url dropping just yet, but I’ll return to a separate blog post when that happens. I hope it’s received well.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Acting on suggestions&lt;/p&gt;

    &lt;p&gt;I have become more active in suggesting new things to try at work, for improving our workflow and following through, building scripts etc. The smoke testing effort is one example of this, but also trying out some suggestions for process changes. Next step here is to encourage my co-workers to do the same also. That is the harder part in this.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;non-tech&quot;&gt;Non-tech&lt;/h3&gt;

&lt;p&gt;I count actually doing non-tech related stuff also a big part of this. Time is short when the agenda is hogged by kids, but there are two big things planned this spring. First is doing a little house refurnishing and actually building something from scratch, not just mounting Ikea furniture. The second is taking another step in performance on the mountain bike. More hours on the bike seat and working on improving the quality of the training as well. And we’ll see how that plays out in the end of May when the first race is on.&lt;/p&gt;

&lt;p&gt;All in all I would definitely say that this plan has improved my focus and I also find it brings more creativity with it. I have never come up with this many ideas of things to try before and also having the energy to try them too, is just great.&lt;/p&gt;

&lt;p&gt;I am a happy camper.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Getting My Son to Read With Technology</title>
   <link href="http://blog.knuthaugen.no//2012/01/getting-my-kid-to-read-with-html5"/>
   <updated>2012-01-18T20:35:46+01:00</updated>
   <id>http://blog.knuthaugen.no//2012/01/getting-my-kid-to-read-with-html5</id>
   <content type="html">&lt;p&gt;I have the exact same challenge as &lt;a href=&quot;http://www.webjournalist.no/2011/11/prosjekt-leselyst-del-1/&quot;&gt;Kenneth Kvalvik&lt;/a&gt; (article in Norwegian): A 6 year old boy who is starting to read but can’t find anything he likes to read. He hates home work assignments (and who can blame him, they’re boring). And as the son of the author of the article above, my son also loves Star Wars. He hasn’t seen any of the films but plays star wars games quite diligently on his Nintendo DS and plays with Star Wars lego a lot.&lt;/p&gt;

&lt;p&gt;So why not give it a go?&lt;/p&gt;

&lt;p&gt;The originator bought a star wars book and translated it and added images from the movies. I skipped that step and went straight to the movies. In part because after reading for only a couple of months (and having only covered about half the alphabet in detail) my son needs very simple text.&lt;/p&gt;

&lt;p&gt;One tough question is: which order should I present the movies in? Original trilogy first or prequel first? I feel every fiber of my Star Wars fanboy body resisting, but I chose prequel trilogy first. Anything else would probably add to the confusion.&lt;/p&gt;

&lt;h3 id=&quot;the-plan&quot;&gt;The Plan&lt;/h3&gt;
&lt;p&gt;My son also loves using the IPad, so I thought I’d take advantage of that and create an ebook app (or “app”) for that platform. I also needed to be able to add more pages quickly (if it was a hit). So I landed on using plain HTML5 and just creating a web page and use the “add to home screen” feature and make it look like an app.&lt;/p&gt;

&lt;p&gt;I am using &lt;a href=&quot;http://swipejs.com/&quot;&gt;swipe.js&lt;/a&gt; which is very lightweight javascript library for just doing swipe gestures on the IPad. I checked out both Sencha Touch and JQTouch (which I have used before) but for this small project they were both overkill.&lt;/p&gt;

&lt;p&gt;The layout of the app is a simple directory structure, one folder for a book, with one sub folder for a chapter with images and videos for that chapter. Each chapter is one html file, with divs that swipe.js is showing and hiding as you swipe. Each page has either an image or a video, and from one to three short sentences in uppercase (written in lowercase and text-transformed with css, so I can lowercase it again when he’s better at reading).&lt;/p&gt;

&lt;p&gt;I went with using movie screen shots for some pages (and the parts where we need some censorship of gory details (not much gore in Star Wars but for a six year old it can be a bit scary)). I also added some small movie clips to some of the pages to keep his interest up and give him a little break in the reading. I researched som HTML5 video players, but found that they all, as per usual, sucked in their own unique way, and went with the safari default video player on the IPad. Which works wonderfully for my use.&lt;/p&gt;

&lt;p&gt;Another takeaway is how much you can cut away from those movies (especially the prequel trilogy that, well, isn’t all that good) without losing the core of the history. The second movie with all that sticky love stuff between Anakin and Padme will be cut even more. 6yo boys are not that into romance.&lt;/p&gt;

&lt;h3 id=&quot;results&quot;&gt;Results&lt;/h3&gt;
&lt;p&gt;So how did this turn out? I created two chapters of the first book before starting a beta test on my first (and only (non-paying)) customer. He read everything in one sitting and immediately asked for more chapters. And that question was repeated every day over the following weeks where I hadn’t had the time to write more chapters. Never had such a pushy user before!&lt;/p&gt;

&lt;p&gt;Technically I have some duplication in the code, specifically the chapter menu. The layout isn’t all that great either, but I plan to fix that soon. The customer was so demanding that I was forced to take on some technical debt to get the product out into the market :-) I have also planned to add slideshow variant to make images clickable and go full screen.&lt;/p&gt;

&lt;p&gt;Now we’re at the end of the first book, and he is asking when the second books becomes available. So I would say in terms of getting a reluctant 6 year old to read, it’s a huge success. He want’s to read it again too. He comes for the action sequences in the video clips, but there are no protests when I say we should read too.&lt;/p&gt;

&lt;p&gt;(Since these movies are all copyrighted, and in these dire #SOPA times, I can’t publish the finished results anywhere, I’m afraid. The text, in Norwegian, is in the code over at Github, but the images and videos are not. And won’t be.)&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A #smidig Retrospective</title>
   <link href="http://blog.knuthaugen.no//2011/11/a-smidig-retrospective"/>
   <updated>2011-11-16T08:52:30+01:00</updated>
   <id>http://blog.knuthaugen.no//2011/11/a-smidig-retrospective</id>
   <content type="html">&lt;p&gt;(This was written just after the Smidig conference but got lost in daily life and something else (probably very important!). Just posted it to get it out of the way)&lt;/p&gt;

&lt;p&gt;I attended the 5th Norwegian agile conference &lt;a href=&quot;http://smidig2011.no/&quot;&gt;Smidig&lt;/a&gt; in November, and as any good agilist, I thought I’d do a little retrospective for the conference. Often the good ideas flurry at the conference, but when reality sets in when you return to work, the good ideas have a tendency to move to the back of the consciousness and not get tried. So, in order to prevent that here’s a list of impressions and more importantly a list of things to try out at work.&lt;/p&gt;

&lt;h3 id=&quot;1-pull-based-meetings&quot;&gt;1. Pull based Meetings&lt;/h3&gt;
&lt;p&gt;This is an idea I had before the conference, but it solidified during it. Instead of running regularly scheduled demos, plannings and backlog grooming meetings, make them happen on-demand. Demo when you have something to demo, estimate and ready stories when backlog is getting slim. And perhaps use shorter, more frequent meetings instead of longer, infrequent meetings. Long meetings suck.&lt;/p&gt;

&lt;h3 id=&quot;2-code-review-as-step-on-the-board&quot;&gt;2. Code Review as Step on The Board&lt;/h3&gt;
&lt;p&gt;We have an informal, voluntary code review step, but use is infrequent and varies a lot. After a lunch talk with my former colleague &lt;a href=&quot;http://rune-sundling.blogspot.com/&quot;&gt;Rune Sundling&lt;/a&gt;, who work in kanban heavy shop, I thought it worth a try to create a code review step before “done”. Wonder how the team will react to the suggestion, though :-)&lt;/p&gt;

&lt;h3 id=&quot;3-defining-requirements-quantitatively&quot;&gt;3. Defining requirements quantitatively&lt;/h3&gt;
&lt;p&gt;I didn’t catch Tom Gilbs talk at smidig, but I know his ideas on this subject. And I am in a position to try this out now. A lot of work will have to go into finding the right quantitative measures for the requirements, but I like the idea and I think it fits our domain and prioritization process well.&lt;/p&gt;

&lt;p&gt;Btw, the talk videos are already available on the &lt;a href=&quot;http://vimeo.com/smidig&quot;&gt;smidig vimeo account&lt;/a&gt; (first videos appeared the same day the talks were held!)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>JavaZone, New Job and the Case of the Missing Blog Post</title>
   <link href="http://blog.knuthaugen.no//2011/08/javazone-new-job-and-the-case-of-the-missing-blog-post"/>
   <updated>2011-08-30T21:32:31+02:00</updated>
   <id>http://blog.knuthaugen.no//2011/08/javazone-new-job-and-the-case-of-the-missing-blog-post</id>
   <content type="html">&lt;p&gt;I’m working hard these days putting the finishing touches on my presentation for this years &lt;a href=&quot;http://jz11.java.no/&quot;&gt;JavaZone&lt;/a&gt; conference. It is a privilege to be able to talk there and I thank the organizer for giving me a full hour on the topic. I will be talking on the subject of deployment pipelines using &lt;a href=&quot;http://jenkins-ci.org/&quot;&gt;Jenkins CI&lt;/a&gt; as the tool, and going through a lot of techniques and scenarios for evolving a pipeline to match your context. And reasons for creating a pipeline in the first place. I gave a lightning talk at the Roots conference in May on the same topic, but there was a lot more to be said than fitted in ten minutes. Hope to see a lot of people there!&amp;lt;div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;And on the subject of continuous delivery: I wrote two blog posts on CD and deployment pipelines a while ago and promised a third one. The third one &lt;i&gt;is&lt;/i&gt; coming, but I won’t make any promises as to when. I am starting a new job September 1st, at &lt;a href=&quot;http://apdm.no/&quot;&gt;APressen Digitale Medier&lt;/a&gt; and I am guessing things will be hectic. Mountain biking season is soon ending and I guess I’ll get some more time when winter hits. I’ll also be spending the winter doing some more ruby stuff and doing HTML5 and other front end work on the &lt;a href=&quot;http://munin-monitoring.org/&quot;&gt;munin project&lt;/a&gt; and the upcoming 2.0 release.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Roots 2011 Braindump II</title>
   <link href="http://blog.knuthaugen.no//2011/05/roots-2011-braindump-ii"/>
   <updated>2011-05-24T12:37:48+02:00</updated>
   <id>http://blog.knuthaugen.no//2011/05/roots-2011-braindump-ii</id>
   <content type="html">&lt;p&gt;Second day of &lt;a href=&quot;http://rootsconf.no/&quot;&gt;Roots 2011&lt;/a&gt; is over and here are my highlights:&lt;/p&gt;

&lt;h3 id=&quot;the-7-duties&quot;&gt;The 7 Duties&lt;/h3&gt;
&lt;p&gt;Opening keynote of the day was &lt;a href=&quot;http://noop.nl/&quot;&gt;Jurgen Appelo&lt;/a&gt; with a talk on the 7 duties of a software professional. Basically how and why you should work on improving yourself in every aspect: motivating yourself, direct yourself, connect yourself (to others), measure yourself and so on. A previously recorded version of this talk, with jokes about the Germans instead of the Swedes is &lt;a href=&quot;http://vimeo.com/22510300&quot;&gt;on the internet&lt;/a&gt;. All in all a great talk and nice advice for moving forward instead of standing still in your life and career.&lt;/p&gt;

&lt;h3 id=&quot;graph-goodness&quot;&gt;Graph Goodness&lt;/h3&gt;
&lt;p&gt;After that came Jim Webbers Neo4j hacking tutorial by going through (some of) the &lt;a href=&quot;https://github.com/jimwebber/neo4j-tutorial&quot;&gt;neo4j koans&lt;/a&gt; koans to get a feel for the neo4j API and how to program it. Didn’t have time to cover a whole lot, but I will try to go through the rest of Koans back home. I have used Neo4j a bit earlier, but I want to get the feel of it in a larger context and use the API more thoroughly.&lt;/p&gt;

&lt;h3 id=&quot;secrets-of-agile-architecture&quot;&gt;Secrets of Agile Architecture&lt;/h3&gt;
&lt;p&gt;Dan North was on once again after lunch with a talk on Secrets of Agile Architecture. He was all over the map talking about topics such as treating organisations as systems and how changing the architecture changes the organisation, managing impact instead of obsessing about likelihood of options and choices. Conway’s law and Dan’s corollary to it, Real Options, PDCA, the role of the architect as a minstrel in modern software development and much much more. Dan is never short on ideas. Some cognitive overload happened there and I need some time to digest all the ideas. Nice talk.&lt;/p&gt;

&lt;h3 id=&quot;haml-and-sass&quot;&gt;HAML And SASS&lt;/h3&gt;
&lt;p&gt;Anders Norås then again took over with a tuturial on using HAML and SASS for writing web applications. Great intro and kudos for creating usb sticks with virtual image with pre-installed software. I have used these two technologies a bit before but I joined anyway to pick up some new tricks. Nothing revolutionary there, but a nice reminder that I use it to seldom in daily life. Need to prioritize my rails projects now that the talk for Roots is out of the way.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Roots 2011 braindump I</title>
   <link href="http://blog.knuthaugen.no//2011/05/roots-braindump-i"/>
   <updated>2011-05-23T13:29:58+02:00</updated>
   <id>http://blog.knuthaugen.no//2011/05/roots-braindump-i</id>
   <content type="html">&lt;p&gt;I'm here at the &lt;a href=&quot;http://rootsconf.no/&quot;&gt;Roots 2011 conference&lt;/a&gt; in Bergen and the program is packed with wonderful stuff. So I thought I'd venture into liveblogging/core dumping the interesting stuff I see.&lt;/p&gt;

&lt;h3&gt;A Way North!&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://dannorth.net/&quot;&gt;Dan North &lt;/a&gt;kicked of the ball with the opening keynote titled Patterns of Effective Delivery and North is entertaining as always. He outlined several patterns he has observed which goes against a lot of conventional wisdom in the agile world. Like starting writing a test named &quot;public void testBlah()&quot; and just start coding. And &lt;i&gt;then&lt;/i&gt;&amp;nbsp;renaming it later on when you see if the code is worth keeping. Or starting a new piece of code by copying and pasting (Ginger Cake Pattern). And possibly refactor out common pieces much later, when you see where the code is taking you. All in all a great talk and I will dig deeper into Dan's findings. The keynote will be online in video form later if all goes well, too.&amp;nbsp;
&lt;/p&gt;

&lt;h3&gt;Jim Webber: Rest in Practice&lt;/h3&gt;
&lt;p&gt;Jim Webber, Chief scientist for Neo Technologies, makers of fine graph databases, was next with a 1 1/2 hour rant on SOA and REST. In short. SOA sucks, REST is good, which is hard to disagree with. And a lot of colourful English idioms was picked up during the talk as well. Great fun, but a bit lacking in nutritional content, perhaps.
&lt;/p&gt;

&lt;h3&gt;Lightning Round!&lt;/h3&gt;
&lt;p&gt;Lots of small gems here, too long to go in depth on all of them. &lt;a href=&quot;http://johannesbrodwall.com/&quot;&gt;Johannes Brodwall&lt;/a&gt;started off with a talk on how to be an agile architect with some nice tips. Andreas Heim talked about virtualizing your development environment for a more rapid startup and better control over your infrastructure, using vagrant, chef/puppet/linger. After that Filip Van Laenen talked about outsourcing self-discipline to the computer and running stuff such as static code analysis metrics as a part of your development process. Interesting take! The first session was rounded of be total geekery on GPU-accelerating normal apps using NVidia CUDA processors.&lt;/p&gt;

&lt;h3&gt;Second Round&lt;/h3&gt;
&lt;p&gt;Second slot was the track on continuous delivery, started off by Stein Inge Morisbak on version control branching strategies for making rapid/continuous delivery possible. Followed by his colleague Ole-Christian Rynning on techniques for automation and delivery: promote binaries, create built-in health checks and let the team deploy. Then there was me talking about deployment pipelines in Jenkins (slides can be found on &lt;a href=&quot;http://www.slideshare.net/knuthaug/cranking-ci-to-11-deployment-pipelines&quot;&gt;slideshare.net&lt;/a&gt;) and the session was rounded off by Mario Aparicio on facts and speculation about NoSQL. After doing quite a bit with of work with various NoSQL databases, I found that last one to be a bit shallow and lacking.&lt;/p&gt;

&lt;h3&gt;Third Round&lt;/h3&gt;
&lt;p&gt;Third round was a fantastic talk by &lt;a href=&quot;https://twitter.com/anoras&quot;&gt;@anoras&lt;/a&gt; (Anders Nor&amp;aring;s) on the notion that we are to busy sharpening the saw/following the next trend to get things done. And even getting things done is a trend. He rounded off by handing out certification documents certifying all in the audience as Bul Shi artists, an ancient Japanese technique. Great fun. Thommy Bommen followed up with a really nice talk on be a responsible person in life and in work, which was followed by a meta-talk on automating lightning talks and getting people to give them inside your own company. Another fine talk by Eirik Bj&amp;oslash;rsn&amp;oslash;s. Last talk was Ellen D. Varsi on hitting the nail on the head and finding the right abstractions for software and getting it right.&lt;/p&gt;

&lt;h3&gt;Open Spaces&lt;/h3&gt;

&lt;p&gt;The open spaces didn't work as well as I have grown accustomed to at Smidig. I think a lot of people went home before them. So few ideas and small groups, but it turned out OK. First slot i used to hack on couchDB replication (dead simple and cool) and &lt;a href=&quot;http://couchapp.org/page/index&quot;&gt;CouchApp&lt;/a&gt; with a few of the local bergen couch mafia. Good fun. Last slot was the question of are we doing to much automated testing through the GUI. It turned into testing strategies and at the end Dan North popped up and joined too.&lt;/p&gt;

&lt;p&gt;And then there was beer and music...&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Continuous Delivery II: Smoketests in Ruby and Rails</title>
   <link href="http://blog.knuthaugen.no//2011/04/continuous-delivery-ii-smoketests-in-ruby-and-rails"/>
   <updated>2011-04-04T19:33:03+02:00</updated>
   <id>http://blog.knuthaugen.no//2011/04/continuous-delivery-ii-smoketests-in-ruby-and-rails</id>
   <content type="html">&lt;p&gt;This is part II in a blog mini series on building a build pipeline with Jenkins. The other parts, &lt;a href=&quot;http://blog.knuthaugen.no/2011/04/continuous-delivery-the-ruby-way.html&quot;&gt;part I&lt;/a&gt; and part III could be worth a read too.&lt;/p&gt;

&lt;p&gt;This part is about a implementing built-in smoke tests in a rails application. This Is by no means new, but I added a little something at the end which I thought was quite nifty.&lt;/p&gt;

&lt;h3 id=&quot;class-definition&quot;&gt;Class definition&lt;/h3&gt;

&lt;p&gt;I created a small class to hold the actual smoke tests (pardon my ruby, I’m learning):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Smoketest&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:message&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;descr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;descr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OK&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;OK&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FAIL&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;FAIL&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;These are called from the SmoketestController. I chose to just put all (well all two) smoke tests in the controller, but they could easily be distributed and put wherever you want. The controller methods looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'smoketest'&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SmoketestController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;mongodb_smoke_test&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test_test&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;respond_to&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;html&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'index.xml.haml'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;ss&quot;&gt;:layout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_test&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Smoketest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Smoketest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OK&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mongodb_smoke_test&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Smoketest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MongoDB test connection&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Smoketest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;OK&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Smoketest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FAIL&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The “test” one is just for making it more than one, and the mongoDB test tries to do a query and gives it and ok status if that works,  and a fail with stack trace if somethings amiss. In a bigger application with more external systems, the number of smoke tests would go up. Running this, gives this nice web page for visual inspection:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.knuthaugen.no/assets_c/2011/04/smokes-26.html&quot; onclick=&quot;window.open('http://blog.knuthaugen.no/assets_c/2011/04/smokes-26.html','popup','width=822,height=203,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false&quot;&gt;&lt;img src=&quot;http://blog.knuthaugen.no/assets_c/2011/04/smokes-thumb-600x148-26.png&quot; width=&quot;600&quot; height=&quot;148&quot; alt=&quot;smokes.png&quot; class=&quot;mt-image-none&quot; style=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(The layout is a bit wide since I wrapped in the standard layout for the app.)&lt;/p&gt;

&lt;h3 id=&quot;the-junit-output&quot;&gt;The Junit output&lt;/h3&gt;
&lt;p&gt;I wanted the jenkins build job to track these smoke tests, which are being run as a build step after deployment. The easiest way to do this I figured was to make it spit out Junit XML format and let Jenkins chew on that. So a created an XML template like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testsuite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;smoketests&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:failures&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;FAIL&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:skipped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@tests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testcase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OK&quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And when fetching the url http://server/smoketest.xml you get the following output:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;testsuite&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;failures=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;smoketests&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;skipped=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;tests=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;testcase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MongoDB test connection&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/testcase&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;testcase&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Test&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/testcase&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/testsuite&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(It handles failures too).&lt;/p&gt;

&lt;p&gt;The following small shell script is used to check the status:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;usage: smoketest.sh &amp;amp;lt;URL&amp;amp;gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;--silent&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--head&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;status was other than 200: was &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$status&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi

if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reports&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;reports&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#put the xml version of the page into a file&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;--silent&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt; &amp;amp;gt&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; reports/smoke.xml&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The shell scripts stores the file which is then read by Jenkins. Works like a charm!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/jenkins-smoke.png&quot; width=&quot;600&quot; height=&quot;256&quot; alt=&quot;jenkins-smoke.png&quot; class=&quot;mt-image-none&quot; style=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned for the third article on build pipelines where I explore some future possibilities when using a build pipeline.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Continuous Delivery I: Pipeline Plugin and a Ruby Project</title>
   <link href="http://blog.knuthaugen.no//2011/04/continuous-delivery-the-ruby-way"/>
   <updated>2011-04-04T17:57:44+02:00</updated>
   <id>http://blog.knuthaugen.no//2011/04/continuous-delivery-the-ruby-way</id>
   <content type="html">&lt;p&gt;I have been reading the excellent book &lt;a href=&quot;http://www.amazon.com/Continuous-Delivery-Deployment-Automation-Addison-Wesley/dp/0321601912/&quot;&gt;Continuous Delivery&lt;/a&gt; by Jez Humble and Dave Farley and been dying to try out some of it in practice. Since work these days is filled with big not-so-easy-to-change java legacy systems, a small greenfield ruby project was the way to go.&lt;/p&gt;

&lt;p&gt;This article ran long, so it is split into three parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;part I (this part): Jenkins setup and project setup.&lt;/li&gt;
  &lt;li&gt;part II: &lt;a href=&quot;http://blog.knuthaugen.no/2011/04/continuous-delivery-ii-smoketests-in-ruby-and-rails.html&quot;&gt;Ruby Smoketest Implementation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;part III: Further Build Steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My aim was to deploy the Ruby on Rails project to &lt;a href=&quot;http://heroku.com/&quot;&gt;heroku&lt;/a&gt; which is a nice and easy-to-use cloudy service for hosting ruby apps. Elegant and easy on both web interface and command-line tools.&lt;/p&gt;

&lt;h3 id=&quot;the-project-setup&quot;&gt;The Project Setup&lt;/h3&gt;
&lt;p&gt;The project I just started is a Rails3 app using bundler and running in ruby 1.9.2 using &lt;a href=&quot;http://rvm.beginrescueend.com/&quot;&gt;rvm&lt;/a&gt;. This, it turns out, brings some special considerations to the mix. Just for kicks and because I start to dislike SQL databases more and more, especially for the cases where you don’t really need it, I use MongoDB and Mongoid as the ORM. MongoDB is supported by Heroku so all’s good there.&lt;/p&gt;

&lt;p&gt;One gotcha it is easy to run into with Heroku and a rails app is that you should have your rails app be at the top level of the git repo. It is supported to run with a rails app in a sub directory, as I did, and you can configure it to work via the &lt;code&gt; config.ru&lt;/code&gt; file, but bundler does not seem to run well on heroku when you do that. So keep the rails app on the top level if you can.&lt;/p&gt;

&lt;p&gt;The projects uses &lt;a href=&quot;https://github.com/rspec/rspec&quot;&gt;rspec&lt;/a&gt; for unit tests and some rails-specific integration/functional tests, and &lt;a href=&quot;http://cukes.info/&quot;&gt;cucumber&lt;/a&gt; for end-to-end acceptance style tests. More on that when it comes to the deployment pipeline and jenkins project setup.&lt;/p&gt;

&lt;h3 id=&quot;jenkins-setup&quot;&gt;Jenkins Setup&lt;/h3&gt;
&lt;p&gt;I belong to the school that dictates that the first thing you should do with a project is to deploy it and then setup CI for it and make &lt;em&gt;that&lt;/em&gt; deploy for you. 
The rake plugin for Jenkins supports rvm so we can use rake tasks directly on the project configuration.&lt;/p&gt;

&lt;p&gt;First of all, I needed cucumber and rspec to output junit format test results to make pretty graphs and record status in Jenkins. Cucumber has a handy &lt;code&gt;--format junit&lt;/code&gt;, but rspec does not. Luckily the gem &lt;code&gt;ci_reporter&lt;/code&gt; does the trick for us. But some customs rake tasks are in order. This is the file &lt;em&gt;lib/tasks/jenkins.rake&lt;/em&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:jenkins&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cucumber_report_path&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;reports/features/&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rspec_report_path&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;reports/rspec/&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Cucumber&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'cucumber'&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'db:test:prepare'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cucumber_opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%{--profile default  --format junit --out #{cucumber_report_path}}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:cukes_report_setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;rm_rf&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cucumber_report_path&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mkdir_p&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cucumber_report_path&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:spec_report_setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;rm_rf&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rspec_report_path&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mkdir_p&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rspec_report_path&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ci&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:report_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jenkins:setup:rspec'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rake:spec'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'cucumber'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:unit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:spec_report_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jenkins:setup:rspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'rake:spec:lib'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:functional&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cukes_report_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;ss&quot;&gt;:spec_report_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;s2&quot;&gt;&quot;jenkins:setup:rspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;s1&quot;&gt;'rake:spec'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                       &lt;span class=&quot;s1&quot;&gt;'cucumber'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pre_ci&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CI_REPORTS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rspec_report_path&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ci_reporter'&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'ci/reporter/rake/rspec'&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:rspec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:pre_ci&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ci:setup:rspec&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It does some setup of of report output directories, some setup of ci_reporter in the :setup task and makes sure rspec tasks does some setup first. The tasks called from jenkins are jenkins:unit for the first build step and jenkins:functional for the second step. The jenkins:ci task is an all in one if that’s your bag. The main point is that the first step runs unit test, hence rake:spec:lib only, and the second step runs all tests and metrics. Ideally if the metric_fu plugin in Jenkins had supported bundler well, I would like to run some metrics in the first step and fail the build if values where below or above a limit. But that does not seem to fly easily.&lt;/p&gt;

&lt;p&gt;I used a small shell script to deploy to Heroku and the test server instead of using the git publisher plugin. This is to make the deployment build step fail the build. The git publisher plugin is run as a post build action and this won’t fail the build. Likewise, the smoke test script needs to run as a build step since the recording of test results is done post build.&lt;/p&gt;

&lt;p&gt;And here’s where the ideals of Continuous Delivery (CD) kicks in. The book advocates doing the build in several steps and the central point is fast feedback. The first step is compile, fast-running unit tests, metrics and building of artifacts (if you’re on a platform that does that). And if all that passes should you go on to step two using &lt;em&gt;the same artifacts&lt;/em&gt; created in step one, possibly aided by a repository manager of some sort to take care of the files in between steps.&lt;/p&gt;

&lt;p&gt;If the first step fails you get unit level and metric violations feedback. If the second fails, you get system level functionality feedback. If later steps involving e.g. performance testing and manual testing you get feedback on those. But you don’t go to that step with a bad build from step one.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://code.google.com/p/build-pipeline-plugin/&quot;&gt;build pipeline plugin&lt;/a&gt; basically gives you a view and a(nother) way of specifying upstream/downstream projects. The view looks like this after it is configured according to &lt;a href=&quot;http://www.wakaleo.com/blog/312-build-pipelines-with-jenkinshudson&quot;&gt;this article&lt;/a&gt; with the four build steps in place.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/jenkinspipe.png&quot; width=&quot;600&quot; height=&quot;228&quot; alt=&quot;Jenkins pipeline&quot; class=&quot;mt-image-none&quot; style=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The build pipeline plugin is in version 1.0.0 and has some major drawbacks. The main one being that it doesn’t stop the pipeline when one step fails. This is a bug and it has been reported on the issue tracker already. I would guess this is fixed in the next release. Another is that it does not seem to be easy to pass params from one build step to the next. There has been some mention of using the parameterized build plugin for that, but haven’t had a chance to test it yet and I don’t know how well the two plugins play well together.&lt;/p&gt;

&lt;h3 id=&quot;the-build&quot;&gt;The Build&lt;/h3&gt;
&lt;p&gt;So in my case, the first step runs unit tests (spec:lib) and the second runs all spec, cucumber features and metric_fu for metrics. The third step deploys to my local test server and runs an automated smoke test. See part II for details on the implementation of that. The output is in junit format, so Jenkins records it and outputs it along with the other tests. If that test brake, the build in step 3 brakes too. I would like to rollback to the previous version on build failure, but I’m not there yet. &lt;a href=&quot;http://casperfabricius.com/site/2009/09/20/manage-and-rollback-heroku-deployments-capistrano-style/&quot;&gt;This article&lt;/a&gt; on using capistrano looks interesting in that regard&lt;/p&gt;

&lt;p&gt;The fourth step deploys to heroku via a script and runs the same smoke test as the step before.&lt;/p&gt;

&lt;h3 id=&quot;benefits-and-ending-thoughts&quot;&gt;Benefits and Ending Thoughts&lt;/h3&gt;
&lt;p&gt;The biggest benefit of using a pipeline is more fine grained feedback on the build process. The compile step is fast and fails almost immediately. The separate steps for integration tests and deployment to test server and heroku gives more accurate feedback on those steps too. I mean, combining deployment to test and production in a single step would be nonsensical. And you can do the deployment to production (or test) a manual one, which the plugin supports. For instance only after manual testing in the test environment. Combined with Jenkins’ matrix style security model you can grant access to certain users for running that build step. There are a lot of possibilities and I will be exploring more of them in part three of the series. Part two is implementation of the smoke tests in ruby.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Rewriting Fixtures in Migrations?</title>
   <link href="http://blog.knuthaugen.no//2011/02/rewriting-fixtures-in-migrations"/>
   <updated>2011-02-25T13:29:31+01:00</updated>
   <id>http://blog.knuthaugen.no//2011/02/rewriting-fixtures-in-migrations</id>
   <content type="html">&lt;p&gt;I had an idea in a twitter discussion with &lt;a href=&quot;http://twitter.com/#!/olecr&quot;&gt;@olecr&lt;/a&gt; about automating different aspects of the development process, coupled with and in-house discussion at work on maintaining database test data for integration tests, which can be a real pain. How about hooking into migrations to rewrite/update fixture data when migrating database schema?&lt;/p&gt;

&lt;p&gt;In ruby one approach to test data, is to use fixtures, often written in yaml, to define test data sets, which gets inserted into the test database before running tests. But you still have to evolve the fixtures when the schema evolves. And the more often it changes, the more of a maintenance nightmare you got on your hands. Other approaches can be to have object factories that create data in the database, or SQL script that gen run (which is what yaml fixtures are, only in a more sane and not-so-verbose format). But the maintenance is there no matter what the approach.&lt;/p&gt;

&lt;p&gt;And then the idea struck me: if you use fixtures for inserting test data into the database before running tests (or between tests) and migrations to modify the schema of the database as you develop the application, couldn’t you create a clever hack that modifies the fixtures to match the migrations? All in one operation? And you can minimize the manual maintenance to the stuff that is really hard to automate and to sanity check to results.&lt;/p&gt;

&lt;p&gt;Yaml is machine-readable format, so one approach could be to read the migrations, find the diff for the migration in question, read the yaml fixtures, modify them and write them out again. In ruby/rake this could be some custom rake tasks that squeezes in with the migrations or something to that effect.&lt;/p&gt;

&lt;p&gt;I would think this could work for things like deleting columns from a table (delete that key in the yaml file), adding new columns (add columns in fixture with dummy value according to data type) and column and table renaming. Some user edits would be necessary nonetheless.&lt;/p&gt;

&lt;p&gt;What do you think? Viable idea or just plain stupid?&lt;/p&gt;

&lt;p&gt;The logical next step of rewriting the test code using these fixtures to match the new schema, well I leave that as an exercise to the reader… :-)&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Certified Keyboard Driven Developer</title>
   <link href="http://blog.knuthaugen.no//2010/12/certified-keyboard-driven-developer"/>
   <updated>2010-12-22T14:55:47+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/12/certified-keyboard-driven-developer</id>
   <content type="html">&lt;p&gt;In the latest episode of &lt;a href=&quot;http://agiletoolkit.libsyn.com/&quot;&gt;The Agile Toolkit&lt;/a&gt; podcast in an interview with &lt;a href=&quot;http://www.davethomas.net/index.html&quot;&gt;Dave Thomas&lt;/a&gt; about, well just about everything, they talk jokingly about the notion of a certification for keyboard shortcuts and I thought, wow, that is just a fantastic idea! This would be a really useful certification as opposed to the many completely or nearly completely useless certifications out there. None mentioned none forgotten.&lt;/p&gt;

&lt;p&gt;Shortcuts makes you more productive all over the place. In your IDE, on the command line and all sorts of other tools. Not having to remove your hand from the keyboard in order to use the mouse saves you time and concentration. You start out by typing, move your one hand to use the mouse, possibly moving your eyes to see the mouse, then moving the mouse pointer to the menu item or button you want, clicking it and then moving your hand back to the keyboard. When the shortcut is second nature you type, enter shortcut and continue typing. No lateral movement of neither hand(s) nor eyes (except to see result of command, but that is true of not using the shortcut too).&lt;/p&gt;

&lt;p&gt;The effect is more flow in your work. Most developer know when they are in the zone and want to stay there to keep the code flowing. Every little distraction should be avoided to maintain the state of flow. Agreed, using the mouse is not a big distraction from the flow, but everything helps, right?&lt;/p&gt;

&lt;p&gt;If you get repetitive stress injuries from using the mouse, using the keyboard could help make them go away. Just make sure you get a good quality keyboard, adjust your desk and chair to the correct height and take frequent short brakes.&lt;/p&gt;

&lt;h3 id=&quot;so-how-should-we-do-this-in-practice&quot;&gt;So How Should We Do This in Practice?&lt;/h3&gt;

&lt;p&gt;There is the trouble of all the different sets of keyboard shortcuts for different tools, of course. Mac users have an advantage here since the platform streamlines a bunch of shortcuts. Tools like IntelliJ IDEA and Resharper which lets you choose between different shortcut schemes (us Emacs users love the Emacs shortcut scheme, at least I do, but sadly it doesn’t quite fit in IntelliJ on the Mac) and you can use the same one in several tools.&lt;/p&gt;

&lt;p&gt;So I guess we need different certifications for different tools, that would make sense. You could then embroider your CV with the title “Certified Mac Keyboard Developer”, Certified Microsoft 2010 Visual Studio Keyboard Core Foundation User” (the Microsoft marketing division can get a say in this, I don’t care), or Certificed Emacs Hacker.&lt;/p&gt;

&lt;p&gt;And the actual test? Give the candidate a list of tasks to to (normal stuff every developer needs like opening files, navigating project structure, running unit test, build/package/deploy, the most common refactoring shortcuts, snippets, code formatting, line navigation and editing and other useful stuff. And just simply disable the mouse or remove it altogether. If they’re done within the time frame, they’re certified! I see a million dollars coming my way with this scheme.&lt;/p&gt;

&lt;h3 id=&quot;command-line-users-have-the-edge&quot;&gt;Command Line Users Have the Edge&lt;/h3&gt;

&lt;p&gt;Users accustomed to using the command line for all sorts of tasks are used to remembering commands and shortcuts to greater extent than “normal” windows users (I use the command line in windows too, but not the crummy built-in cmd.exe) and could get certified more easily. The history navigation in the unix shell, combined with aliases and piping is awesome for productivity.&lt;/p&gt;

&lt;h3 id=&quot;training-for-the-exam&quot;&gt;Training For the Exam&lt;/h3&gt;
&lt;p&gt;There are some techniques for training for this kind of certification. First there are cheat sheets that should decorate your desktop or cubicle wall and &lt;a href=&quot;http://www.cheat-sheets.org/&quot;&gt;http://www.cheat-sheets.org/&lt;/a&gt; has a huge collection. Some java IDEs have plugins that will remind you of the fact that a shortcut exists if you select a menu item with the mouse. Some can even be configured to disable the menu item if you won’t take the hint. Lastly just time yourself doing something you do every day and avoid the mouse altogether and see the effects for yourself.&lt;/p&gt;

&lt;p&gt;Disabling the mouse for some period of time could also be tried. It hurts in the beginning, but you should quickly get over it. &lt;a href=&quot;http://weblogs.asp.net/jgalloway/archive/2006/06/14/Mouseless-Computing.aspx&quot;&gt;Jon Galloway&lt;/a&gt; also has some good suggestions in his blog post on mouseless computing.&lt;/p&gt;

&lt;p&gt;I for one try to use the mouse as little a possible.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Log4net, Windows Event Log and IIS Applications</title>
   <link href="http://blog.knuthaugen.no//2010/12/log4net-windows-event-log-and-iis-applications"/>
   <updated>2010-12-17T07:56:37+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/12/log4net-windows-event-log-and-iis-applications</id>
   <content type="html">&lt;p&gt;I am doing a C# project at the moment where we are using &lt;a href=&quot;http://logging.apache.org/log4net/index.html&quot;&gt;log4net&lt;/a&gt; to log to the Windows event log.  From the windows service this is straight forward but it turns out when you are trying to do this from an application running in IIS, a WCF service in our case, this is surprisingly hard. So I thought I’d document this one for later reference.&lt;/p&gt;

&lt;h2 id=&quot;those-pesky-permissions&quot;&gt;Those Pesky Permissions&lt;/h2&gt;

&lt;p&gt;Just duplicating the config for a normal windows application doing perfectly good event logging isn’t going to work as you will quickly discover. A good way to debug this is to set up a FileAppender to log to a temporary directory somewhere. Log lines ends up there when the config is read OK, but not in the event log. It’s a permission thing. The user running IIS probably has got privileges to &lt;strong&gt;log&lt;/strong&gt; to the event log, but not create the event source initially. Nor should it, since running IIS as a privileged user is security disaster waiting to happen. Don’t do it.&lt;/p&gt;

&lt;p&gt;The config used for logging in this example is this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;log4net&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;debug=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;root&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;priority&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INFO&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EventLogAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EventLogAppender&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;log4net.Appender.EventLogAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;applicationName&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My Application&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;layout&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;log4net.Layout.PatternLayout&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;conversionPattern&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%date [%thread] %-5level %logger [%property{NDC}] - %message%newline&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/layout&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;/log4net&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;the-solution&quot;&gt;The Solution&lt;/h2&gt;

&lt;p&gt;Step one is to create the event source by hand as a normal user. You can do this via the following code in a C# app.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;n&quot;&gt;EventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateEventSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;My Application&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or do it from the command line using the &lt;code&gt;eventcreate&lt;/code&gt; program&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;c:\&amp;gt; eventcreate /ID 1 /L APPLICATION /T INFORMATION /SO &quot;My Application&quot; /D &quot;Dummy log message&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This gives you a “dummy log message” in the application log. And you should be good to go. But not so fast. Eventcreate requires an EventID between 1 and 1000. Default logging from log4net for some reason uses event id 0, which will give you an event log error message in the application log. It contains your log message but it looks like a mess. So how to persuade log4net to use event id 1, which we used when creating the event source?&lt;/p&gt;

&lt;p&gt;There are two ways of doing this. One is to use the log4net extension log4net.Ext.EventID and configure it there. If that isn’t your cup of tea, you can quite easily, although a bit hackish, do it directly in your code before any log messages are sent with this piece of code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;n&quot;&gt;log4net&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;EventID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For instance in a constructor or other suitable initialization code.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Talking at Smidig2010</title>
   <link href="http://blog.knuthaugen.no//2010/10/talking-at-smidig2010"/>
   <updated>2010-10-29T15:18:24+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/10/talking-at-smidig2010</id>
   <content type="html">&lt;p&gt;I’m super excited that I got one (so far, they normally only accept one per person) of my abstracts for the Norwegian Agile conference &lt;a href=&quot;http://smidig2010.no/&quot;&gt;Smidig 2010&lt;/a&gt; in Oslo 16. and 17. of November. This is my first proper conference talk even though I have been giving talks in other context to large audiences in the past.&lt;/p&gt;

&lt;p&gt;The talk title is hard to translate but the topic is on the agility of NoSQL databases in general and document databases in particular and I will be contrasting old school relational databases to new school document databases in areas such as management, testing, development friction etc.&lt;/p&gt;

&lt;p&gt;And the conference in itself is super cool with three tracks of four 10-minute lightning talks before lunch and open spaces crowd sourced by the participants after lunch, both days. I attended previous two years and it’s a blast.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>All Your Detection Belongs to Modernizr</title>
   <link href="http://blog.knuthaugen.no//2010/09/all-your-detection-belongs-to-modernizr"/>
   <updated>2010-09-22T12:19:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/09/all-your-detection-belongs-to-modernizr</id>
   <content type="html">&lt;p&gt;I’ll hopefully be doing some HTML5 work soon and I came across the 
 &lt;a href=&quot;http://www.modernizr.com/&quot;&gt;Modernizr&lt;/a&gt; project, which is very cool for doing feature detection as opposed to browser detection. It does all the heavy (well, not so heavy but tedious) lifting when it comes to detecting if the browser visiting your site supports all the fancy schmancy new features from HTML5 and CSS3. Like websockets, CSS 2D and 3D transforms, web workers, canvas, offline storage, browser databases, video and audio elements and much more awesomeness. Check the link for the complete list. And it’s really easy to use too. The nice thing about it is that it does not accomplish this through browser detection, which is very unreliable, but rather through checking for the feature directly by trying to call API functions or creating stuff.&lt;/p&gt;

&lt;h3&gt;Let's detect!&lt;/h3&gt;

&lt;p&gt;Load it like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;modernizr-1.5.min.js&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and the next time you need to know it some feature is supported, check the automagically created Modernizr object and see if that property is true or false:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Modernizr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As a nice bonus it assigns css classes to the &lt;code&gt;html&lt;/code&gt; element so you can do different styling based on whether a feature is available or not.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;c&quot;&gt;/* In your CSS: */&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.no-audio&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#audio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;/* Don't show Audio options */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;.audio&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#audio&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;c&quot;&gt;/* Style the Play and Pause buttons nicely */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
 </entry>
 
 <entry>
   <title>Twitter Data and Neo4j-rest in Pure Javascript</title>
   <link href="http://blog.knuthaugen.no//2010/08/twitter-data-and-neo4j-rest-in-pure-javascript"/>
   <updated>2010-08-11T00:00:00+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/08/twitter-data-and-neo4j-rest-in-pure-javascript</id>
   <content type="html">&lt;h2 id=&quot;the-stage&quot;&gt;The Stage&lt;/h2&gt;
&lt;p&gt;Some free time prompted me to test out the &lt;a href=&quot;http://components.neo4j.org/neo4j-rest/&quot;&gt;REST interface&lt;/a&gt; for the most excellent graph database Neo4j. And to gives those rusty javascript skills a refresh, I’d thought I’d to it in pure javascript. To help see what is going on, I visualize the graph using &lt;a href=&quot;http://thejit.org/&quot;&gt;JIT&lt;/a&gt; and use jQuery under the hood to do the rest calls and the DOM scripting more efficiently.&lt;/p&gt;

&lt;p&gt;I start out simple by just fetching and using data accessible in the public twitter API. The model I choose was to query twitter for friends and followers of a particular user, store it in neo4j and plot it as a directed graph on a HTML5 canvas element using JIT. Next time around the graph can be fetched directly from neo4j without going to twitter. Everything is in javascript and the only html involved is a input field for the username to search for and a HTML5 canvas element for drawing the graph using JIT.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;Browser-side javascript can be hard to develop and debug. I chose JSpec as my testing framework for the day, which has a nice DSL for writing RSpec-like tests and has support for running in all browser through a command-line tool. It Also has rhino support.&lt;/p&gt;

&lt;p&gt;JSpec tests are cool. JSpec comes bundled in a ruby gem which you install and after that you get a command line runner for firing browsers, or rhino, and looking up syntax and doing other neat stuff to your test suite. It looks like a good project with steady releases and progress. Here’s an example of how a test looks with the DSL in action:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;twitter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;before&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;network&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;neo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;neo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;node_exists&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                           &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://localhost:9999/node/1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;neo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;get_node&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;before_each&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;twitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Twitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;neo&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;neo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;twitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;get_request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
       &lt;span class=&quot;nx&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;twitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lookup()&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;should call callback on fetch for valid user&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;stub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;neo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;get_node&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;nx&quot;&gt;twitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;knuthaug&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;in_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
                   &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;in_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;eql&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;
   &lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;patterns&quot;&gt;Patterns&lt;/h2&gt;

&lt;p&gt;Javascript gives you plenty of ammunition to shoot yourself in the foot, and I decided to follow a pattern from Douglas Crockfords book “Javascript, The Good Parts” and implement my objects with what we can call the “that” pattern. Instead of defining the object like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
           &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;//and call it like this&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myNode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;we write it like this.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;

   &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

   &lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
           &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
           &lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;that&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//and call it like so:&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;myNode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This encapsulates all variables and functions into a variable which is returned to the caller and if we were to implement private functions on the object, something javascript normally doesn’t let you do, we define them on “this” instead of “that”.  The spec argument to the constructor is just an easy way of passing in default values to the object being created, or giving it a prototype to inherit from. the “new” keyword normally does this for you and with this technique you have to do it yourself to get a prototype chain set up.&lt;/p&gt;

&lt;p&gt;The point is that I’m not sure I’m fond of this technique since I find the code to get a bit muddled and the “that” keyword has a strange ring to it. Next time round I’ll probably stick to the normal way of setting up objects.&lt;/p&gt;

&lt;p&gt;I ended up with and object encapsulating the graph, one for the node, one wrapping the twitter API and lastly one wrapping neo4j-rest for storing and retrieving data. The code, if you’re interested can be found on &lt;a href=&quot;http://github.com/knuthaug/twitter-navigator&quot;&gt;github&lt;/a&gt; (very unpolished and probably won’t ever be polished so take it with a grain of salt or two).&lt;/p&gt;

&lt;h2 id=&quot;gephi&quot;&gt;Gephi&lt;/h2&gt;
&lt;p&gt;Gephi is an open source tool for visualizing a graph using Open GL for fast and fancy rendering capabilities. A version released a short time ago (&lt;a href=&quot;http://gephi.org/2010/gsoc-2010-mid-term-adding-support-for-neo4j-in-gephi/&quot;&gt;get it here&lt;/a&gt;) has support for connecting directly to a (sadly, not running) instance of Neo4J and import the whole graph.&lt;/p&gt;

&lt;p&gt;For this little pet project the main focus for using Gephi was to test it out and see how it works, but the potential is huge for graphs with more data in them. Gephi can do all sorts of graph manipulation and analysis and showing the graph according to values stored in the nodes etc. In the twitter graph I stored the number of followers in each of my followers as a number and Gephi could easily be configured to draw the node size relative to that, color edges based on data and much more. And all in nice openGL smooth graphics performance.&lt;/p&gt;

&lt;p&gt;All in all a great little adventure. And you should learn proper javascript :-)&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Comparing MongoDB java frameworks</title>
   <link href="http://blog.knuthaugen.no//2010/07/comparing-mongodb-java-frameworks"/>
   <updated>2010-07-28T11:13:34+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/07/comparing-mongodb-java-frameworks</id>
   <content type="html">&lt;p&gt;I while ago I gave a talk at a Oslo JavaBin meeting on NoSQL and in the research for that I wounded up writing some sample code for three different frameworks for using &lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt; from Java. The code is &lt;a href=&quot;http://github.com/knuthaug/mongodb-samples/tree/java&quot;&gt;available on GitHub&lt;/a&gt;. The three are the 10Gen mongo driver (and not really a framework), &lt;a href=&quot;http://code.google.com/p/morphia/&quot;&gt;morphia&lt;/a&gt; and &lt;a href=&quot;http://github.com/jannehietamaki/mungbean&quot;&gt;mungbean&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-whole-dynamic-thing&quot;&gt;The Whole Dynamic Thing&lt;/h3&gt;

&lt;p&gt;MongoDB is a schema-free document database and does not impose any restriction on what you can store in a collection. There is nothing stopping you from puttting entirely different objects into the same collection and thus nothing stopping you from omitting fields in two different objects of the same kind either. So how does this play with Javas strong static typing compared to a dynamic language like Ruby?&lt;/p&gt;

&lt;h3 id=&quot;dude-wheres-my-orm&quot;&gt;Dude, Where’s My ORM?&lt;/h3&gt;
&lt;p&gt;Developers used to relational databases have been using ORMs for a long time to abstract away the dirty details of dealing with the database and separating the domain from the persistence. So, are any of the three frameworks an ORM for MongoDB? Well, no. But a more interesting question is: Do I need one? MongoDB is a very different beast than oracle/mysql/MSsql/PostgreSQL and different beasts need different handling. You need &lt;em&gt;an&lt;/em&gt; abstraction model for MongoDB, not necessarily the &lt;em&gt;ORM&lt;/em&gt; abstraction model.&lt;/p&gt;

&lt;h3 id=&quot;mongodb-java-driver&quot;&gt;MongoDB Java Driver&lt;/h3&gt;

&lt;p&gt;10Gen java driver basically gives you to options when storing data in MongoDB: Either subclass &lt;strong&gt;BasicDBObject&lt;/strong&gt; which is the general database object, or implement the &lt;strong&gt;DBObject&lt;/strong&gt; interface. Both approaches gives you the interface of &lt;strong&gt;Map&lt;/strong&gt; from the standard java library and lets you put and get keys and values. The drawback is that there are a lot of methods in this interface and it will soon become tedious to implement them all for different domain objects. This it the &lt;strong&gt;DBObject&lt;/strong&gt; interface methods:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;putAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DBObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;putAll&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Map&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;toMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;removeField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;containsKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;containsField&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keySet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markAsPartialObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isPartialObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So I’d prefer to subclass &lt;strong&gt;BasicDBObject&lt;/strong&gt; instead or wrap it.&lt;/p&gt;

&lt;p&gt;So how does code using it look like? Assume the very minimal domain objects &lt;strong&gt;Person&lt;/strong&gt; and &lt;strong&gt;Address&lt;/strong&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.direct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.BasicDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BasicDBObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fullName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fullName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.direct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.BasicDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BasicDBObject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

   &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;postalcode&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;place&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;country&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;place&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
   
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Using this class is done like this in the form of functional/integration tests, since they touch the database but written in Junit:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.direct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.BasicDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.DB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.DBCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.Mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.After&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;java.net.UnknownHostException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersonPersistance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testDb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;DBCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Before&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnknownHostException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;nc&quot;&gt;Mongo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27017&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

       &lt;span class=&quot;n&quot;&gt;testDb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testDb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;persons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setObjectClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;insertPersonSavesPersonToDatabase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                 &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0401&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                             &lt;span class=&quot;s&quot;&gt;&quot;Oslo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Norge&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;personWithDocumentNotMatchingObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
      &lt;span class=&quot;nc&quot;&gt;BasicDBObject&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BasicDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tmp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;retrievePersonFromDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0401&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                                              &lt;span class=&quot;s&quot;&gt;&quot;oslo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Norge&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;oslo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@After&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Notice the call to &lt;strong&gt;setObjectClass()&lt;/strong&gt; on the collection object to get “type-safe” operations even though you still have to cast the return value from &lt;strong&gt;findOne()&lt;/strong&gt; to get you precious object back. Other than that it is pretty straight forward. Call &lt;strong&gt;insert()&lt;/strong&gt; on the collection to insert an object, &lt;strong&gt;findOne()&lt;/strong&gt; or any other query method to retrieve it. But to the bottom line is this driver really begs for some more abstraction when you’re beyond toy samples. The positive effect is more direct access to the data which is often the way it’s done in dynamic languages. But does that suit Java? I’m not sure but tend to think no. And how about those nulls? Well, if one object doesn’t store a value in a field, the field is null when returned from the database, as you can see from the test &lt;strong&gt;personWithDocumentNotMatchingObject&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;morphia&quot;&gt;Morphia&lt;/h3&gt;
&lt;p&gt;Morphia is a (according to the blurb) a light-weight type-safe mapper for MongoDB providing DAO&amp;lt;T, V&amp;gt; and Datastore abstractions. It takes the annotation approach. Here’s our &lt;strong&gt;Person&lt;/strong&gt; and &lt;strong&gt;Address&lt;/strong&gt; classes for Morphia:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.morphia&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.code.morphia.annotations.Embedded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.code.morphia.annotations.Entity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.code.morphia.annotations.Id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Entity&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Id&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Embedded&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.morphia&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.code.morphia.annotations.Embedded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@Embedded&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;streetName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;postalCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;place&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Where Address is annotated as an &lt;em&gt;embedded&lt;/em&gt; object. This is the same approach taken by the ruby Mongo_Mapper with its MongoMapper::Document and MongoMapper::EmbeddedDocument classes.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.morphia&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.google.code.morphia.Morphia&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.DB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.DBCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.mongodb.Mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.After&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;java.net.UnknownHostException&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PersistanceThroughMorphia&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;Morphia&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Mongo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;DBCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Before&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UnknownHostException&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Morphia&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27017&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;// This is where we map Persons and addresses&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;// But shouldn't the annotation be able to handle that?&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;no&quot;&gt;DB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testDb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mongo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getDB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testDb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;persons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;storePersonThroughMorphiaMapping&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0401&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Oslo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Norge&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;personMissingField&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0401&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Oslo&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Norge&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
 
        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;toDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;morph&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;fromDBObject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assertNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    

    &lt;span class=&quot;nd&quot;&gt;@After&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It seems like something out of the department of redundancy department that you annotate the document and the embedded document and then have to specify the relationship between them in a method call. My first reaction was that that would have been cleaner if the relationship could be specified in the annotation too. The calls to &lt;strong&gt;morph.toDBObject()&lt;/strong&gt; and &lt;strong&gt;morph.fromDBObject()&lt;/strong&gt; breaks up an otherwise elegant solution. It also introduces some more code and it basically wraps up a cast. That could have been a lot cleaner.&lt;/p&gt;

&lt;h3 id=&quot;mungbean&quot;&gt;Mungbean&lt;/h3&gt;
&lt;p&gt;Mungbean is our last contestant and represents a third way of doing the mapping. It wraps up &lt;em&gt;everything&lt;/em&gt; you need for accessing MongoDB with generic collection classes and introduces a DSL for querys and the like. There’s also a clojure version if that is more like your poison. The domain classes with mungbean:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.mungbean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;streetName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;streetName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;postalCode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;postalCode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;place&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;country&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;place&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Nothing special here, no imports and no annotations. (Almost the) Same with Person except for the import and field of type &lt;strong&gt;ObjectID&lt;/strong&gt; which handles the object ids generated by mongo on insert:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.mungbean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.ObjectId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ObjectId&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ObjectId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;


  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ObjectId&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Using it on the other hand, creates a very different looking code than the other two, thanks to the wrapper classes for mongodb connections, the generic collections and the query DSL:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;no.kh.mongo.mungbean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.DBCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.Database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.Mungbean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.Settings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;mungbean.query.Query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.After&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Before&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.junit.Test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;junit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;framework&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;mungbeanPersistence&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Database&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testPerson&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DBCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;


  &lt;span class=&quot;nd&quot;&gt;@Before&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mungbean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Settings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27017&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;openDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;testPerson&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;openCollection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;persons&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;storePerson&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testPerson&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


  &lt;span class=&quot;nd&quot;&gt;@Test&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;personGetGeneratedIdAndAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testPerson&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;found&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Knut Haugen&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertNotNull&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;found&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getAddress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;josefines gate&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;


  &lt;span class=&quot;nd&quot;&gt;@After&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dbAdmin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;dropDatabase&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The syntax is nice but perhaps a tad verbose for mye taste. I find the abstraction quite good, at least better than the other two. I also like the fact that there is almost no trace of the library in the domain classes and as such it is by far the best of the three.&lt;/p&gt;

&lt;h3 id=&quot;the-verdict&quot;&gt;The Verdict&lt;/h3&gt;

&lt;p&gt;It’s Mungbean, by a nose! Mainly because of the cleaner domain objects and the DSL. There is more code involved but I found it to be more elegant than the other approaches. I want to note that both morphia and mungbean are not immensely mature and &lt;em&gt;done&lt;/em&gt; by any definition of the word and that has to come into consideration when using them. And it may be that a wordy statically typed language like Java has a bit of friction with a very dynamic database backend like MongoDB. I don’t know, but I’ll be looking into ruby drivers in the future and we’ll see.&lt;/p&gt;

&lt;p&gt;I haven’t looked into the different code generation frameworks for mongo, namely &lt;a href=&quot;http://java.dzone.com/articles/using-mongodb-sculptor&quot;&gt;Sculptor&lt;/a&gt; and &lt;a href=&quot;http://github.com/mattinsler/com.lowereast.guiceymongo/&quot;&gt;GuicyData&lt;/a&gt; which take a different approach to accessing MongoDB. That’s for another time and another post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Book Overflow</title>
   <link href="http://blog.knuthaugen.no//2010/04/a-book-overflow"/>
   <updated>2010-04-30T15:53:44+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/04/a-book-overflow</id>
   <content type="html">&lt;p&gt;I am a habitual book buyer and I kinda like the paper kind which is evident if you see my office shelf or my living room. So once again I bought more books than I can read in the time I have left from living my normal life.&lt;/p&gt;

&lt;h3 id=&quot;growing-software&quot;&gt;Growing software&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Growing-Object-Oriented-Software-Guided-Signature/dp/0321503627/&quot;&gt;Growing Object-oriented Software Guided by Tests&lt;/a&gt; By Steve Freeman and Nat Pryce. This is a book I’ve only heard positive things about and the TOC seem to deliver on that. It offers a lot of details on the how and more important the why of TDD/BDD and testing in relation to development. I am &lt;em&gt;so&lt;/em&gt; looking forward to this one.&lt;/p&gt;

&lt;h3 id=&quot;being-productive&quot;&gt;Being Productive&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Productive-Programmer-Theory-Practice-OReilly/dp/0596519788&quot;&gt;The Productive Programmer&lt;/a&gt; by Neal Ford. I’m a productivity junkie just like Neal Ford and some of the tricks (and the first part of the book is a long list of tools and tips) are already in my tool belt. But there’s always more to learn, all in the spirit of kaizen. He throws in some good advice on software design also, all in the spirit of being productive.&lt;/p&gt;

&lt;h3 id=&quot;the-art-of-agile&quot;&gt;The Art of Agile&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Art-Agile-Development-James-Shore/dp/0596527675/&quot;&gt;The Art of Agile Software Development&lt;/a&gt; by James Shore and Chromatic. Another one I have heard a lot about and all positive. By the first look it seems to cover the state of agile software development today and touches on some lean and TPS-inspired concepts too. And you can’t really navigate around that in today’s agile landscape.&lt;/p&gt;

&lt;h3 id=&quot;effective-java&quot;&gt;Effective Java&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Effective-Java-Second-Joshua-Bloch/dp/0321356683/&quot;&gt;Effective Java second edition&lt;/a&gt; by Joshua Block. I’ll be writing more Java in the coming months as I’ll be changing jobs so this is in order to brush up on the old java skills. Also a book that is widely regarded as a must.&lt;/p&gt;

&lt;h3 id=&quot;the-legacy-you-inherit&quot;&gt;The Legacy You Inherit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052/&quot;&gt;Working Effectively with Legacy Code&lt;/a&gt; by Michael Feathers. How do you test legacy apps? Highly recommended by a colleague who recently inherited a largish legacy codebase with only a tauntingly small amount of tests. What’s the definition of legacy code? Code without tests :-) Even if you write it today…&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Cooking with Mongodb and Solr</title>
   <link href="http://blog.knuthaugen.no//2010/04/cooking-with-mongodb-and-solr"/>
   <updated>2010-04-27T13:46:28+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/04/cooking-with-mongodb-and-solr</id>
   <content type="html">&lt;p&gt;I’ve recently changed storage backend and search backend for a small web project and it has been a real blast.  What follows is an overview of the reasons for the change, what the change actually was and the relative amount of joy involved.&lt;/p&gt;

&lt;h3 id=&quot;the-old-system&quot;&gt;The Old System&lt;/h3&gt;

&lt;p&gt;System was built using PHP/Apache2 and MySQL and it covers a &lt;em&gt;very&lt;/em&gt; simple domain with only a single object (Person, sort of) and simple data records for several years.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No writing through the web interface, only search and lookup.&lt;/li&gt;
  &lt;li&gt;Batch updates with between 4 and 5 million records in each update, 4-5 years history, so total 16-20 million records&lt;/li&gt;
  &lt;li&gt;“Search” through very simple text indexes on the relevant columns in MySQL.&lt;/li&gt;
  &lt;li&gt;InnoDB backend&lt;/li&gt;
  &lt;li&gt;File is transformed to &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/load-data.html&quot;&gt;LOAD DATA INFILE&lt;/a&gt; format and fed into MySQL with manual delete of the set for that year beforehand.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-pain-points&quot;&gt;The Pain Points&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Batch update with 4 million rows averages (on prod machine: dual core 3GHz 4GB Ram, roughly 1GB set aside for MySQL) taking 4-5 hours hours with index updates being the main culprit. This could be done as a check-the-record-and-update-if-changed but that would also require a lot of queries and updates to the database.&lt;/li&gt;
  &lt;li&gt;Queries with wildcards are dead slow when hitting outside the query cache.&lt;/li&gt;
  &lt;li&gt;Not really advanced search as such.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-plan&quot;&gt;The Plan&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Replacing MySQL with &lt;a href=&quot;http://www.mongodb.org/&quot;&gt;MongoDB&lt;/a&gt; as there is no actual relations needed and everything fits in one collection of documents&lt;/li&gt;
  &lt;li&gt;Replacing MySQL indexes with &lt;a href=&quot;http://lucene.apache.org/solr/&quot;&gt;Apache Solr&lt;/a&gt; for consolidating search across several other systems. And speed.&lt;/li&gt;
  &lt;li&gt;Use the PECL extensions for both &lt;a href=&quot;http://pecl.php.net/package/mongo&quot;&gt;MongoDB&lt;/a&gt; and &lt;a href=&quot;http://pecl.php.net/package/solr&quot;&gt;Solr&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MongoDB is a document database storing documents in binary json form, written in C++.&lt;/p&gt;

&lt;p&gt;Solr is built on top of the java version of Lucene and does indexing over HTTP and runs happily in tomcat, jetty or most other servlet engines.&lt;/p&gt;

&lt;h3 id=&quot;the-implementation&quot;&gt;The Implementation&lt;/h3&gt;

&lt;p&gt;Names of domain objects are changed to protect the guilty - and the domain.&lt;/p&gt;

&lt;p&gt;Both Solr and MongoDB are &lt;em&gt;fast and easy&lt;/em&gt; to work with. There is very little in your way when it comes to just doing what you want and solving the problems in a straight-forward manner. Some examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The MongoDB “upsert” feature saves you some round-trips to the database. Normally if you want to update an existing record if it’s there or inserting it if it’s not you need to query first and then insert a new one when not found. If you just want to update/add data to part of the object, it complicates the matter further. With mongodb you can call update with a special parameter in the data array and the rest is handled server-side.&lt;/li&gt;
  &lt;li&gt;Solr has the default behavior of updating instead of complaining when you send a document with a primary key field that already exists in the index.&lt;/li&gt;
  &lt;li&gt;Solr does everything over HTTP and you get easy-to-read xml message back as responses. This is also handy when you need to debug what data is sent over the wire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created a very thin layer between mongodb and the domain, with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insert()&lt;/code&gt; method (which as we will see, also handles updates) that take a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataRecord&lt;/code&gt; (read from the file) as an argument.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;DataRecord&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'id'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
           &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'$set'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
               &lt;span class=&quot;s1&quot;&gt;'list.'&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getDetails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())),&lt;/span&gt; 
           &lt;span class=&quot;k&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'upsert'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will insert a document in the collection if it’s not there. When it is there, it will add an element to the (nested) ‘list’ element with the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$record-&amp;gt;year()&lt;/code&gt; as key. The value will be the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$record-&amp;gt;getDetails()&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toArray()&lt;/code&gt; call is there because the mongo driver expects arrays to store. The super cool part is that if the key exists, it will just be updated with the data from the details object. Read more on the details of the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Updating&quot;&gt;MongoDB update options&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For indexing the document in Solr, I added a similarly thin wrapper for the SolrClient object with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index()&lt;/code&gt; method. This method takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SolrInputDocument&lt;/code&gt; as an argument. I chose to delegate to the domain object to decide what should be indexed and thus create the index document object but the responsibilities could easily have switched around. The finer point is that when indexing you have to read the complete object from the database in order to get all data. The DataRecord that was read from file and stored with upsert may just have been part of the picture. Reading back the updated object incurs a performance penalty that wasn’t present in the old system. It was also a consequence of structuring the data as a collection of person objects in Mongodb, rather than a long list of records in the old version. This maps better to the domain.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;SolrInputDocument&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;solr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;addDocument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pendingDocuments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commitInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pendingDocuments&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Commit on every Solr document makes indexing very slow. Small tests indicated 3 minutes for indexing 5000 documents with commit on every submit and 15 seconds with one commit every 2000 document (and at the end of course). The code above commits every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$commitInterval&lt;/code&gt;(10000 default) to speed things up a bit. Note also that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optimize()&lt;/code&gt; calls for Solr may time out as they can take a long time to finish. Solr does not time out but rather the java application server you’re running times out. When this happens an exception is thrown in the php driver which has to be caught.&lt;/p&gt;

&lt;h3 id=&quot;the-results&quot;&gt;The Results&lt;/h3&gt;
&lt;p&gt;Platform is Ubuntu 9.10 server edition 64 bit and all timings from the shell are done with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;time&lt;/code&gt; on linux. MySQL times are the times reported from MySQL itself.&lt;/p&gt;

&lt;h3 id=&quot;time-for-batch-insertupdate&quot;&gt;Time for batch insert/update&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Commit interval for solr: 10000&lt;/li&gt;
  &lt;li&gt;update-logging for Solr turned off (default is very verbose)&lt;/li&gt;
  &lt;li&gt;nssize=1024 for Mongodb&lt;/li&gt;
&lt;/ul&gt;

&lt;table class=&quot;blogtable&quot; width=&quot;100%&quot;&gt;
 &lt;thead&gt;
&lt;tr&gt;
  &lt;th&gt;System&lt;/th&gt;
  &lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;tr&gt;
  &lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;initial import&lt;/td&gt;
&lt;td&gt;16m 5.7s&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;update (delete+insert)&lt;/td&gt;
&lt;td&gt;3h 24m 56s (delete) + 40m 56s insert&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;Mongodb (no indexing)&lt;/td&gt;
&lt;td&gt;initial import&lt;/td&gt;
&lt;td&gt;12m 16s real, 10m 41s user&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;Mongodb+solr&lt;/td&gt;
&lt;td&gt;initial import&lt;/td&gt;
&lt;td&gt;78m 39s real, 25m 28s user.&lt;/td&gt;
  &lt;/tr&gt;

&lt;tr&gt;
  &lt;td&gt;Mongodb (no indexing)&lt;/td&gt;
&lt;td&gt;update &lt;/td&gt;
&lt;td&gt;13m 22s real, 11m 8s user.&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;Mongo+solr&lt;/td&gt;
&lt;td&gt;update&lt;/td&gt;
&lt;td&gt;69m 8s real, 19m 25 user&lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;space-usage&quot;&gt;Space usage&lt;/h3&gt;

&lt;p&gt;No pre-allocation was done for MongoDB so it created the data files as needed. This means that the last was created at 2GB and very well may be almost empty. Mongo creates files in a doubling fashion from 64 MB to 2G like this: 64, 128, 256, 512, 1GB, 2GB.&lt;/p&gt;

&lt;table class=&quot;blogtable&quot; width=&quot;100%&quot;&gt;
 &lt;thead&gt;
&lt;tr&gt;
  &lt;th&gt;System&lt;/th&gt;
  &lt;th&gt;Index&lt;/th&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;tr&gt;
  &lt;td&gt;MySQL initial import&lt;/td&gt;
&lt;td&gt;717 MB&lt;/td&gt;
&lt;td&gt;516 MB&lt;/td&gt;
  &lt;/tr&gt;

 &lt;tr&gt;
  &lt;td&gt;MySQL initial import + 1 additional dataset&lt;/td&gt;
&lt;td&gt;1.4 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;Mongo+Solr initial import&lt;/td&gt;
&lt;td&gt;744 MB&lt;/td&gt;
&lt;td&gt;1294 MB (3GB of datafiles)&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
  &lt;td&gt;Mongo+Solr initial + 1 additional dataset.&lt;/td&gt;
&lt;td&gt;1538 MB&lt;/td&gt;
&lt;td&gt;4348 MB&lt;/td&gt;
  &lt;/tr&gt;


 &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;cpu-usage&quot;&gt;CPU usage&lt;/h3&gt;

&lt;p&gt;When importing to MySQL it more or less maxes on CPU for the entire import. When doing the import with a php script feeding data to mongodb and solr, the component using the most cpu is the php script splitting the file, creating objects and calling the mongodb and solr APIs.This takes up 35-40% CPU and around 10 MB of ram.  Mongodb is using around 10% cpu (with 1.1GB of ram) and solr (tomcat, that is) is spending 30% and around 300MB of ram. Disks on these machines are virtualized, through Vmware, 15K SAS disks on an IBM S3200 storage array with dedicated GB LAN between blade center and storage. Disk IO seems to be the bottleneck here.&lt;/p&gt;

&lt;h3 id=&quot;space-usage-1&quot;&gt;Space usage&lt;/h3&gt;

&lt;p&gt;Solr and mongodb seem to be a bit more sloppy with their space usage than MySQL but I guess this is the price to pay for some of the other benefits you get. See &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Developer+FAQ#DeveloperFAQ-Whyaremydatafilessolarge?&quot;&gt;mongo faq on data files&lt;/a&gt; for info on how to see real space usage for databases and not just file sizes. In return for more storage space spent, you get &lt;em&gt;much&lt;/em&gt; better search capabilities (and faster) and faster (although small improvement) query times against database.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Naming Classes and Interfaces</title>
   <link href="http://blog.knuthaugen.no//2010/04/naming-classes-and-interfaces"/>
   <updated>2010-04-16T08:45:58+02:00</updated>
   <id>http://blog.knuthaugen.no//2010/04/naming-classes-and-interfaces</id>
   <content type="html">&lt;p&gt; The naming of interfaces and corresponding implementation classes in languages that use them (of which Java and C# is the most used I guess) is a subject of sometimes heated debate. A growing distaste of the &quot;best practice&quot; (at least on Microsofts part) of using a capital &quot;I&quot; on interfaces prompted this rundown and a round of pro et contra. My ultimate goal is readability of code but a slight predicament emerges when considering a team: if the majority of team members are used to one particular convention is that, in that particular context, more readable &lt;em&gt;for that team&lt;/em&gt; than another approach, all other things being equal? Even though the name of the interface reads more like natural English? The jury's still out on this one. 
&lt;/p&gt;
&lt;p&gt;The different patterns I’ve found:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Interfaces are prefixed with a capital I, where &lt;code&gt;IReportGenerator&lt;/code&gt; is the interface and e.g. &lt;code&gt;ReportGenerator&lt;/code&gt; is the implementation. Some argue for making the interface name read &quot;I generate reports&quot; and keep the I prefix, thus: &lt;code&gt;IGenerateReports&lt;/code&gt; instead. &lt;/li&gt;
&lt;li&gt;Ditching the capital I and appending &quot;imp&quot; or &quot;impl&quot; to the implementation. Like so: &lt;code&gt;UserManager&lt;/code&gt; and &lt;code&gt;UserManagerImpl&lt;/code&gt;. Some use uppercase or lowercase &quot;c&quot; as prefix for concrete classes too but I find that horribly ugly, to be frank. &lt;/li&gt;
&lt;li&gt; Naming an interface for it's role and naming the implementing class(es) for what distinguishes it from (possibly) other implementations. E.g. &lt;code&gt;UserManager&lt;/code&gt; for the interface and &lt;code&gt;DatabaseUserManager&lt;/code&gt; or &lt;code&gt;LDAPUserManager&lt;/code&gt; for the concrete implementations. &lt;/li&gt;
&lt;li&gt;Naming abstract class with the word &quot;Abstract&quot; as a prefix to make a distinction for abstract and concrete classes. Possibly combined with other conventions for interfaces as well. I would argue the context often reveals an abstract class to the reader. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt; C# vs Java&lt;/h2&gt;
&lt;p&gt;In C# the extension of an abstract base class &lt;em&gt;and&lt;/em&gt; the implementation of an interface uses the same syntax and which one it is isn't always easy to discern. A naming convention makes this a bit easier:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DatabaseUserManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UserManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//versus&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DatabaseUserManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IUserManager&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In java the syntax is different:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot; data-lang=&quot;java&quot;&gt;&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LDAPUserManager&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserManager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//versus&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LDAPUserManager&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserManager&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;
The need for a prefix for readability is less needed in Java, compared to C#.
&lt;/p&gt;

&lt;h2&gt;Good names trumps conventions?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882&quot;&gt;Clean Code&lt;/a&gt; by Robert C. Martin argues for good names: good names for variables, good names for methods and good names for interfaces and classes. The name should reveal intention. &lt;i&gt;Choosing good names takes time but saves more than it takes&lt;/i&gt;. (Clean Code, page 18). The book also mentions not using the &quot;I&quot; prefix and prefers to encode the implementation, if at all.  &lt;/p&gt;
&lt;p&gt;The question is: if the names are as good as they can be, do we need a prefix or suffix or other kinds of encoding to indicate the type? In my mind we don't. I find that the I hurts readability and the &quot;impl&quot; certainly doesn't look good to me. So how to choose good names?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Classes should have noun names or noun phrases, e.g. &lt;code&gt;Customer&lt;/code&gt;, &lt;code&gt;Policy&lt;/code&gt; or &lt;code&gt;StreetAddress&lt;/code&gt;. 
&lt;li&gt;Use name from the problem domain. E.g. If you're dealing with a customer or client object. In a medical system, should it be called &lt;code&gt;Patient&lt;/code&gt;? Or if it's dealing with social benefits maybe &lt;code&gt;BenefitsReceiver&lt;/code&gt; is a better name? The domain influences the choice of names. In stock-broking perhaps the name &lt;code&gt;FuturesHolder&lt;/code&gt; is name candidate for a special kind of customer? (note: I'm not very familiar with either of these domains so the examples may be somewhat off. You get the point: know your domain and find good names from it.).
&lt;/li&gt;
&lt;li&gt;Use names from the solution domain. If you use e.g. the &lt;a href=&quot;http://en.wikipedia.org/wiki/Visitor_pattern&quot;&gt;Visitor pattern&lt;/a&gt;, make it a part of the name(s) so other programmers see it
&lt;code&gt;InsuranceCustomerVisitor&lt;/code&gt; is better than &lt;code&gt;CustomerTraverser&lt;/code&gt;
&lt;/li&gt;

&amp;lt;/ol&amp;gt;

&lt;p&gt;So unless some team coding standard absolutely makes me (and that is, I'm afraid, likely) I will not in the future prefix my interfaces with I and not adorn my implementations with c, impl or any other stuff. Just meaningful, names from the domain. Easier to write and easier to read. &lt;/p&gt;

&lt;p&gt; Lastly, I posted this question on twitter and 
&lt;a href=&quot;http://twitter.com/borud&quot;&gt;@borud&lt;/a&gt; made the comment: &quot;the only useful information conveyed by an I-prefix to interface types is information about the author of the code.&quot; That's a suitable end note I think :-)
&lt;/p&gt; 
&lt;/li&gt;&lt;/ol&gt;
</content>
 </entry>
 
 <entry>
   <title>Analysis  of the NoSQL Landscape</title>
   <link href="http://blog.knuthaugen.no//2010/03/the-nosql-landscape"/>
   <updated>2010-03-17T08:01:47+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/03/the-nosql-landscape</id>
   <content type="html">&lt;p&gt;This is an overview of the current state of the NoSQL landscape. It’s getting large and somewhat unwieldy and there may be projects which have landed in the wrong category here. I have included object databases in the mix too. Seriously folks, some of you need to pick more google friendly project names. Here are the types and the players in each category. Background data is available in &lt;a href=&quot;https://spreadsheets.google.com/ccc?key=0AivRtF1K3Ma7dHFpWjllNVBnNlhyT0Y2WkpDMGdiU2c&amp;amp;hl=en&quot;&gt;this Google docs spreadsheet&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;keyvalue-stores&quot;&gt;Key/value stores&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;key/value in-memory:&lt;/strong&gt; &lt;a href=&quot;http://memcached.org/&quot;&gt;memcached&lt;/a&gt;, &lt;a href=&quot;http://repcached.lab.klab.org/&quot;&gt;Repcached&lt;/a&gt;, &lt;a href=&quot;http://www.oracle.com/technology/products/coherence/index.html&quot;&gt;Oracle Coherence&lt;/a&gt;, &lt;a href=&quot;http://www.jboss.org/infinispan&quot;&gt;Infinispan&lt;/a&gt;, &lt;a href=&quot;http://www-01.ibm.com/software/webservers/appserv/extremescale/&quot;&gt;Websphere eXtreme scale&lt;/a&gt;, &lt;a href=&quot;http://www.jboss.org/jbosscache&quot;&gt;JBoss cache&lt;/a&gt;, &lt;a href=&quot;http://blogs.msdn.com/velocity/default.aspx&quot;&gt;Velocity&lt;/a&gt;, &lt;a href=&quot;http://www.terracotta.org/&quot;&gt;Terracotta Ehcache&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Regular key/value stores:&lt;/strong&gt; &lt;a href=&quot;http://scalien.com/keyspace/&quot;&gt;Keyspace&lt;/a&gt;, &lt;a href=&quot;http://aws.amazon.com/simpledb/&quot;&gt;Amazon SimpleDB&lt;/a&gt;, &lt;a href=&quot;http://labs.gree.jp/Top/OpenSource/Flare-en.html&quot;&gt;Flare&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/schemafree/&quot;&gt;Schema-free&lt;/a&gt;,  &lt;a href=&quot;http://fiz.stanford.edu:8081/display/ramcloud/Home&quot;&gt;RAMCloud&lt;/a&gt;, &lt;a href=&quot;http://twistedstorage.sourceforge.net/&quot;&gt;Twisted Storage (TSnoSQL)&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/redis/&quot;&gt;Redis&lt;/a&gt;, &lt;a href=&quot;http://1978th.net/tokyocabinet/&quot;&gt;Tokyo Cabinet&lt;/a&gt;, &lt;a href=&quot;http://opensource.plurk.com/LightCloud/&quot;&gt;Lightcloud&lt;/a&gt;, &lt;a href=&quot;http://blitiri.com.ar/p/nmdb/&quot;&gt;NMDB&lt;/a&gt;, &lt;a href=&quot;http://luxio.sourceforge.net/&quot;&gt;Lux IO&lt;/a&gt;, &lt;a href=&quot;http://memcachedb.org/&quot;&gt;Memcachedb&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/actord/&quot;&gt;Actord&lt;/a&gt;, &lt;a href=&quot;http://www.oracle.com/technology/products/berkeley-db/index.html&quot;&gt;BerkeleyDB&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/scalaris/&quot;&gt;Scalaris&lt;/a&gt;, &lt;a href=&quot;http://fisglobal.com/Products/TechnologyPlatforms/GTM/index.htm&quot;&gt;GT.M&lt;/a&gt;, &lt;a href=&quot;http://www.erlang.org/doc/apps/mnesia/index.html&quot;&gt;Mnesia&lt;/a&gt;, &lt;a href=&quot;http://hamsterdb.com/&quot;&gt;HamsterDB&lt;/a&gt;, &lt;a href=&quot;http://sourceforge.net/projects/chordless/&quot;&gt;Chordless&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Eventually Consistent key/value stores:&lt;/strong&gt; &lt;a href=&quot;http://en.wikipedia.org/wiki/Dynamo_%28storage_system%29&quot;&gt;Amazon Dynamo&lt;/a&gt;, &lt;a href=&quot;http://project-voldemort.com/&quot;&gt;Voldemort&lt;/a&gt;, &lt;a href=&quot;http://wiki.github.com/cliffmoon/dynomite/&quot;&gt;Dynomite&lt;/a&gt;, &lt;a href=&quot;http://subrecord.org/&quot;&gt;SubRecord&lt;/a&gt;, Mo8onDb, &lt;a href=&quot;http://millstonecw.com/dovetaildb/&quot;&gt;Dovetaildb&lt;/a&gt;, &lt;a href=&quot;http://sourceforge.net/projects/kai/&quot;&gt;KAI&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;column-oriented-stores&quot;&gt;Column-oriented stores&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://labs.google.com/papers/bigtable.html&quot;&gt;Google BigTable&lt;/a&gt;, &lt;a href=&quot;http://hadoop.apache.org/hbase/&quot;&gt;HBase&lt;/a&gt;, &lt;a href=&quot;http://cassandra.apache.org/&quot;&gt;Cassandra&lt;/a&gt;, &lt;a href=&quot;http://hypertable.org/&quot;&gt;HyperTable&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/openneptune/&quot;&gt;OpenNeptune&lt;/a&gt;, KDI, QBase&lt;/p&gt;

&lt;h2 id=&quot;document-databases&quot;&gt;Document Databases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://couchdb.apache.org/&quot;&gt;CouchDB&lt;/a&gt;, &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Home&quot;&gt;MongoDB&lt;/a&gt;, &lt;a href=&quot;http://jackrabbit.apache.org/&quot;&gt;Apache JackRabbit&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/thrudb/&quot;&gt;ThruDB&lt;/a&gt;, &lt;a href=&quot;http://getcloudkit.com/&quot;&gt;CloudKit&lt;/a&gt;, &lt;a href=&quot;http://www.persvr.org/&quot;&gt;Perservere&lt;/a&gt;, &lt;a href=&quot;http://www-01.ibm.com/software/lotus/products/domino/&quot;&gt;Lotus Domino&lt;/a&gt;, &lt;a href=&quot;http://riak.basho.com/&quot;&gt;Riak&lt;/a&gt;, &lt;a href=&quot;http://code.google.com/p/terrastore/&quot;&gt;Terrastore&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;object-databases&quot;&gt;Object Databases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://zodb.org/&quot;&gt;ZODB&lt;/a&gt;, &lt;a href=&quot;http://db4o.com/&quot;&gt;db40&lt;/a&gt;, &lt;a href=&quot;http://www.versant.com/&quot;&gt;Versant&lt;/a&gt;, &lt;a href=&quot;http://www.gemstone.com/&quot;&gt;Gemstone/s&lt;/a&gt;, &lt;a href=&quot;http://web.progress.com/en/index.html&quot;&gt;Progress Objectstore&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;graph-databases&quot;&gt;Graph Databases&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://neo4j.org/&quot;&gt;Neo4j&lt;/a&gt;, &lt;a href=&quot;http://www.dekorte.com/projects/opensource/vertexdb/&quot;&gt;VertexDB&lt;/a&gt;, &lt;a href=&quot;http://infogrid.org/&quot;&gt;Infogrid&lt;/a&gt;, &lt;a href=&quot;http://www.sones.com/home&quot;&gt;Sones&lt;/a&gt;, &lt;a href=&quot;http://sourceforge.net/projects/filament/&quot;&gt;Filament&lt;/a&gt;, &lt;a href=&quot;http://www.franz.com
/agraph/allegrograph/&quot;&gt;Allegrograph&lt;/a&gt;, &lt;a href=&quot;http://www.kobrix.com/hgdb.jsp&quot;&gt;HyperGraphDB&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;projects-by-type&quot;&gt;Projects by Type&lt;/h2&gt;

&lt;p&gt;If we graph all the projects by type we get this view:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;projects_by_type(2).png&quot; src=&quot;../../../images/projects_by_type%282%29.png&quot; width=&quot;597&quot; height=&quot;438&quot; class=&quot;mt-image-center&quot; style=&quot;text-align: center; display: block; margin: 0 auto 20px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are more key/value stores than the other types combined. Why is this? Are key/value stores that much easier to implement? I would at least guess that the first area where we see projects being abandoned and convergence of projects is this one. The important thing is the features users want, not the project themselves. There &lt;strong&gt;must&lt;/strong&gt; be a lot of overlap here and a lot of projects that are slightly different and almost identical. On the other hand a lot of knowledge of these kinds of system is spread around and there is a good chance of innovation. The combination of the best technical features and API features will hopefully bubble to top and stay on.&lt;/p&gt;

&lt;h2 id=&quot;license-breakdown&quot;&gt;License Breakdown&lt;/h2&gt;

&lt;p&gt;If we graph the projects in the list above by license chosen we get the following:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;projects_by_license(2).png&quot; src=&quot;../../../images/projects_by_license%282%29.png&quot; width=&quot;651&quot; height=&quot;452&quot; class=&quot;mt-image-center&quot; style=&quot;text-align: center; display: block; margin: 0 auto 20px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This shows a clear dominance for open source licenses over commercial ones. Some product have chosen a dual licensing model (neo4j and BerkelyDB). Quite a few are unknown which really means they are unable to communicate their license in a understandable manner or the project wasn’t really found on the web at all (see point about google friendly names).&lt;/p&gt;

&lt;h2 id=&quot;language-breakdown&quot;&gt;Language Breakdown&lt;/h2&gt;

&lt;p&gt;Graphing the projects by implementation language we get the following:&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;projects_by_language(4).png&quot; src=&quot;../../../images/projects_by_language%284%29.png&quot; width=&quot;594&quot; height=&quot;445&quot; class=&quot;mt-image-center&quot; style=&quot;text-align: center; display: block; margin: 0 auto 20px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Java takes the lead by with C and C++ following close behind. But is the prevalence of Java a result of the amount of Java knowledge spread around and the big Java usage in Open source, or is Java more suited than other languages to implement these kinds of systems? Interesting to note the number of Erlang implementations and also the fact that quite a few of the projects have implementations in more than one language. The ones with more than one implementation are mostly commercial ones.&lt;/p&gt;

&lt;p&gt;Some ending questions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Have we reach the maximum of projects that are sustainable now or will the ecosystem continue to grow even more?&lt;/li&gt;
  &lt;li&gt;Will more of them go commerical? Or will more choose the model with support as the income, like 10Gen has with MongoDB?&lt;/li&gt;
  &lt;li&gt;How does one choose the right one to use for a given project? This is an increasingly hard problem, at least for key/value stores.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;references&quot;&gt;References:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://dl.getdropbox.com/u/2075876/nosql-steve-yen.pdf&quot;&gt;NoSQL presentation by Steve Yen&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nosql-database.org/&quot;&gt;http://nosql-database.org/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nosql.mypopescu.com/&quot;&gt;http://nosql.mypopescu.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.dbms2.com/2010/03/14/nosql-taxonomy/&quot;&gt;http://www.dbms2.com/2010/03/14/nosql-taxonomy/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.nahurst.com/visual-guide-to-nosql-systems&quot;&gt;http://blog.nahurst.com/visual-guide-to-nosql-systems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;All the various project pages and product websites.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>A Brief History of NoSQL</title>
   <link href="http://blog.knuthaugen.no//2010/03/a-brief-history-of-nosql"/>
   <updated>2010-03-16T14:45:16+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/03/a-brief-history-of-nosql</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/NoSQL&quot;&gt;NoSQL&lt;/a&gt; is getting a lot of traction and hype these days but in reality it’s not that new of a thing. I thought I’d trace the roots of NoSQL and see what I’d find. The name “NoSQL” was in fact first used by Carlo Strozzi in 1998 as the name of file-based &lt;a href=&quot;http://www.strozzi.it/cgi-bin/CSA/tw7/I/en_US/nosql/Home%20Page&quot;&gt;database he was developing&lt;/a&gt;. Ironically it’s relational database just one without a SQL interface. As such it is not actually a part of the whole NoSQL movement we see today. The term re-surfaced in 2009 when Eric Evans used it to name the current surge in non-relational databases. It seems like the name has stuck for better or for worse. Note that not all projects are included in this post. See the &lt;a href=&quot;http://blog.knuthaugen.no/2010/03/the-nosql-landscape.html&quot;&gt;post on analyzing the NoSQL landscape&lt;/a&gt; for a more complete listing.&lt;/p&gt;

&lt;h3 id=&quot;1960s&quot;&gt;1960s&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/MultiValue&quot;&gt;MultiValue&lt;/a&gt; (aka PICK) databases are developed at TRW in 1965.&lt;/li&gt;
  &lt;li&gt;According to comment from Scott Jones &lt;a href=&quot;http://en.wikipedia.org/wiki/MUMPS&quot;&gt;M[umps]&lt;/a&gt; is developed at Mass General Hospital in 1966. It is a programming language that incorporates a hierarchical database with B+ tree storage.&lt;/li&gt;
  &lt;li&gt;IBM &lt;a href=&quot;http://en.wikipedia.org/wiki/IBM_Information_Management_System&quot;&gt;IMS&lt;/a&gt;, a hierarchical database, is developed with Rockwell and Caterpillar for the Apollo space program in 1966.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;##1970s&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;InterSystems develops the ISM product family succeeded by the Open M product, all M[umps] implementations. See comment from Scott Jones below.&lt;/li&gt;
  &lt;li&gt;M[umps] is approved as a ANSI standard language in 1977.&lt;/li&gt;
  &lt;li&gt;in 1979 Ken Thompson creates &lt;a href=&quot;http://en.wikipedia.org/wiki/Dbm&quot;&gt;DBM&lt;/a&gt; which is released by AT&amp;amp;T. At it’s core it is a file-based hash.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1980s&quot;&gt;1980’s&lt;/h2&gt;

&lt;p&gt;Several successors to DBM spring into life.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://tdbm.dss.ca/&quot;&gt;TDBM&lt;/a&gt; supporting atomic transactions&lt;/li&gt;
  &lt;li&gt;NDBM was the Berkeley version of DBM supporting having multiple databases open at the same time.&lt;/li&gt;
  &lt;li&gt;SDBM - another clone of DBM mainly for licensing reasons.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/GT.M&quot;&gt;GT.M&lt;/a&gt; is the first version of a key-value store with focus on high performance transaction processing. It is open sourced in 2000.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Berkeley_DB&quot;&gt;BerkeleyDB&lt;/a&gt; is created at Berkeley in the transition from 4.3BSD to 4.4BSD. Sleepycat software is started as a company in 1996 when Netscape needed new features for BerkeleyDB. Later acquired by Oracle which still sell and maintain BerkeleyDB.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www-01.ibm.com/software/lotus/&quot;&gt;Lotus Notes&lt;/a&gt; or rather the server part, Lotus Domino, which really is a document database has it’s initial release in 1989, now sold by IBM. It has evolved a lot from the early versions and is now a full office and collaboration suite.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1990s&quot;&gt;1990’s&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.vivtek.com/gdbm/&quot;&gt;GDBM&lt;/a&gt; is the Gnu project clone of DBM&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Mnesia&quot;&gt;Mnesia&lt;/a&gt; is developed by Ericsson as a soft real-time database to be used in telecom. It is relational in nature but does not use SQL as query language but rather Erlang itself.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.intersystems.com/&quot;&gt;InterSystems Caché&lt;/a&gt; launched in 1997  and is a hybrid so-called post-relational database. It has object interfaces, SQL, PICK/MultiValue and direct manipulation of data structures. It is a M[umps] implementation. See Scott Jones comment below for more on the history of InterSystems&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Metakit&quot;&gt;Metakit&lt;/a&gt; is started in 1997 and is probably the first document oriented database. Supports smaller datasets than the ones in vogue nowadays.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2000-2005&quot;&gt;2000-2005&lt;/h2&gt;

&lt;p&gt;This is were the NoSQL train really picks up some momentum and a lot is starting to happen.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Graph database &lt;a href=&quot;http://neo4j.org/&quot;&gt;Neo4j&lt;/a&gt; is started in 2000.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.db4o.com/&quot;&gt;db4o&lt;/a&gt; an object database for java and .net is started in 2000&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://qdbm.sourceforge.net/&quot;&gt;QDBM&lt;/a&gt; is a re-implementation of DBM with better performance by Mikio Hirabayashi.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://memcached.org/&quot;&gt;Memcached&lt;/a&gt; is started in 2003 by Danga to power Livejournal. Memcached isn’t really a database since it’s memory-only but there is soon a version with file storage called &lt;a href=&quot;http://memcachedb.org/&quot;&gt;memcachedb&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://infogrid.org&quot;&gt;Infogrid&lt;/a&gt; graph database is started as closed source in 2005, open sourced in 2008&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://couchdb.apache.org/&quot;&gt;CouchDB&lt;/a&gt; is started in 2005 and provides a document database inspired by Lotus Notes. The project moves to the Apache Foundation in 2008.&lt;/li&gt;
  &lt;li&gt;Google &lt;a href=&quot;http://en.wikipedia.org/wiki/BigTable&quot;&gt;BigTable&lt;/a&gt; is started in 2004 and the research paper is released in 2006.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;##2006-2010&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jackrabbit.apache.org/&quot;&gt;JackRabbit&lt;/a&gt; is started in 2006 as an implementation of JSR 170 and 283.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://1978th.net/tokyocabinet/&quot;&gt;Tokyo Cabinet&lt;/a&gt; is a successor to QDBM by (Mikio Hirabayashi) started in 2006&lt;/li&gt;
  &lt;li&gt;The research paper on &lt;a href=&quot;http://www.allthingsdistributed.com/2007/10/amazons_dynamo.html&quot;&gt;Amazon Dynamo&lt;/a&gt;  is released in 2007.&lt;/li&gt;
  &lt;li&gt;The document database &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Home&quot;&gt;MongoDB&lt;/a&gt; is started in 2007 as a part of a open source cloud computing stack and first standalone release in 2009.&lt;/li&gt;
  &lt;li&gt;Facebooks open sources the &lt;a href=&quot;http://cassandra.apache.org/&quot;&gt;Cassandra project&lt;/a&gt; in 2008&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://project-voldemort.com/&quot;&gt;Project Voldemort&lt;/a&gt; is a replicated database with no single point-of-failure. Started in 2008.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://github.com/cliffmoon/dynomite&quot;&gt;Dynomite&lt;/a&gt; is a Dynamo clone written in Erlang.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.google.com/p/terrastore/&quot;&gt;Terrastore&lt;/a&gt; is a scalable elastic document store started in 2009&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.google.com/p/redis/&quot;&gt;Redis&lt;/a&gt; is persistent key-value store started in 2009&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://riak.basho.com/&quot;&gt;Riak&lt;/a&gt; Another dynamo-inspired database started in 2009.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://wiki.apache.org/hadoop/Hbase&quot;&gt;HBase&lt;/a&gt; is a BigTable clone for the Hadoop project while &lt;a href=&quot;http://hypertable.org/&quot;&gt;Hypertable&lt;/a&gt; is another BigTable type database also from 2009.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://github.com/stevedekorte/vertexdb&quot;&gt;Vertexdb&lt;/a&gt; another graph database is started in 2009&lt;/li&gt;
  &lt;li&gt;Eric Evans of Rackspace, a committer on the Cassandra project, introduces the term “NoSQL” often used in the sense of “Not only SQL” to describe the surge of new projects and products.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Some of these dates need to be taken with a small pinch of salt as finding out exactly when the projects started can be a bit difficult. Also not all projects started in last few years have been included)&lt;/p&gt;

&lt;p&gt;In 2009 and 2010 we also saw the coming of NoSQL conferences like &lt;a href=&quot;http://www.10gen.com/events&quot;&gt;NoSQL live&lt;/a&gt; in Boston in 2010, the upcoming &lt;a href=&quot;http://nosqleu.com/&quot;&gt;NoSQL eu&lt;/a&gt; in London in April 2010. Last year we also saw the &lt;a href=&quot;https://nosqleast.com/2009/#location&quot;&gt;NoSQL east&lt;/a&gt; conference in Atlanta.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Employee View of Agile Interviews</title>
   <link href="http://blog.knuthaugen.no//2010/02/employee-view-of-agile-interviews"/>
   <updated>2010-02-07T23:43:23+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/02/employee-view-of-agile-interviews</id>
   <content type="html">&lt;p&gt;I found two interesting blog posts via Twitter, &lt;a href=&quot;http://blog.thirstybear.co.uk/2010/02/don-just-interview-new-developers_03.html&quot;&gt;Just Interview new Developers&lt;/a&gt; and &lt;a href=&quot;http://www.davenicolette.net/agile/index.blog/1947137/the-new-interview/&quot;&gt;http://www.davenicolette.net/agile/index.blog/1947137/the-new-interview/&lt;/a&gt; both covering a new and perhaps more agile (hey! buzzword!) way of doing interviews. The key point is to &lt;em&gt;audition&lt;/em&gt; the candidate rather than to just ask questions. Doing actual pair programming and seeing how people how &lt;em&gt;actually&lt;/em&gt; work to solve a concrete programming tasks is much more valuable than to give them a random task, ask if they remember some obscure part of a spec everybody looks up anyway or not least, see what certifications they have. Would you like to work for a company that put more faith in your certifications than your current abilities?&lt;/p&gt;

&lt;p&gt;Both posts cover this from the viewpoint of the potential employer but I can see just as much value for the employee. When interviewing for a new job you rarely get an accurate view of how the company really is through the interview. The reality really dawns on you a couple of months into the job. My (somewhat limited) experience with interviews is that they are very rarely used to dig deep enough, both from the standpoint of the employer and the employee. But we should! Hiring the wrong people is expensive for the company and choosing to work for the wrong company is just waste of time for an employee. Or worse.&lt;/p&gt;

&lt;p&gt;###The Questions to Ask&lt;/p&gt;

&lt;p&gt;If I where to look for a new job I would milk the interview for all it’s worth and use it to see what kind of company I’m dealing with.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If they’re not doing pair programming in the interview, will they do it in real life?&lt;/li&gt;
  &lt;li&gt;How serious are they about their agility?&lt;/li&gt;
  &lt;li&gt;Do they check what I remember or do they make an effort to find out how I work?&lt;/li&gt;
  &lt;li&gt;Are they interested in the buzzwords I know or the process I use to develop software and solve problems?&lt;/li&gt;
  &lt;li&gt;Are they interested in finding out about the real me?&lt;/li&gt;
  &lt;li&gt;If at all possible: pair program with the people I’m going to be working with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would perhaps even go as far as asking them &lt;em&gt;why&lt;/em&gt; they’re are hiring they way they are, regardless of how, and use the answer to decipher their way of thinking about people and the craft of software development. I mean: would you choose to work for a company which isn’t really interested in finding out the &lt;em&gt;real you&lt;/em&gt; but rather just hire you?&lt;/p&gt;

&lt;p&gt;Some other questions I would consider asking to find out more about the company:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What kind of computer hardware and software are developers using (this can seem like useless geek obsessions, but it tells me something about how they value the productivity of their developers. Best answer: you get to choose :-))&lt;/li&gt;
  &lt;li&gt;How does the company (top to bottom) ensure continuous improvement?&lt;/li&gt;
  &lt;li&gt;How does the company measure the productivity of their developers? (trick question).&lt;/li&gt;
  &lt;li&gt;How far up the management chain does the agile principles really go? Think Toyota Production System-like environment.&lt;/li&gt;
  &lt;li&gt;For a consulting firm: how much/often do you spend time inside the mother ship opposed to on-site at clients?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-time-it-takes&quot;&gt;The Time it Takes&lt;/h3&gt;

&lt;p&gt;Doing interviews in this fashion takes time and I have heard stories (at Hashrocket I think) of week long pair programming sessions with the entire team and one “no” from one team member is all it takes to turn down the candidate. This probably works better in the US with their system of two weeks notice, but not so well in Norway with (normally) three months mutual notice. But for sure: one hour of questions doesn’t tell you enough. Practice trumps talking, talking trumps reading - to paraphrase the kanban saying.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Learning Ruby With Ruby Koans</title>
   <link href="http://blog.knuthaugen.no//2010/01/learning-ruby-with-ruby-koans"/>
   <updated>2010-01-12T15:58:55+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/01/learning-ruby-with-ruby-koans</id>
   <content type="html">&lt;p&gt;I’m learning Ruby these days and after reading the excellent book &lt;a href=&quot;http://www.amazon.co.uk/Ruby-Programming-Language-David-Flanagan/dp/0596516177&quot;&gt;The Ruby Programming Language&lt;/a&gt; I found myself in need of actually writing and reading some code. I tried starting a pet project in Ruby but the, uhm, distractions of two young children made it difficult to get that off the ground and I also quickly realized that I needed some more training before I could get productive on that project.&lt;/p&gt;

&lt;p&gt;So enter &lt;a href=&quot;http://github.com/edgecase/ruby_koans/&quot;&gt;Ruby Koans&lt;/a&gt; written by Jim Weirich and Joe O’Brien of EdgeCase. This little git repo guides (and forces you) through a series of test files with (at first) failing unit tests where you read code and make failing tests pass one after another. It starts out easy with variables and true false and the likes and end up with open classes, message passing and modules.&lt;/p&gt;

&lt;p&gt;If you tolerate some zen puns and need a quick intro to ruby and testing in ruby, it’s worth a look. The beauty for me was the ease of doing one pomodoro of Ruby Koans each morning and the mental context needed to do that was very small.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Systems Thinking applied to software development</title>
   <link href="http://blog.knuthaugen.no//2010/01/systems-thinking-applied-to-software-development"/>
   <updated>2010-01-05T22:29:50+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/01/systems-thinking-applied-to-software-development</id>
   <content type="html">&lt;p&gt;I have watched &lt;a href=&quot;http://www.vimeo.com/4670102&quot;&gt;some&lt;/a&gt; &lt;a href=&quot;http://www.infoq.com/presentations/rethinking-lean-service&quot;&gt;talks&lt;/a&gt; by John Seddon on Systems Thinking and where he feels Lean has failed. Seddon’s focus is service organizations in public and private sector like health-care and customer support and sales for various products. But how would systems thinking apply to software development? Have we got it all wrong? I’m going to try and map some of the ideas of Systems Thinking over to existing software development methodologies and practices and see where we end up.&lt;/p&gt;

&lt;h3 id=&quot;systems-thinking-in-a-nutshell&quot;&gt;Systems Thinking in a nutshell&lt;/h3&gt;
&lt;p&gt;Systems Thinking is about seeing the organization as a system and studying it as a system from the view of the customer. Some core ideas summarized:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The only plan for changing an organization is to get knowledge&lt;/li&gt;
  &lt;li&gt;Study demand going into the system&lt;/li&gt;
  &lt;li&gt;Differ between failure demand (bug reports, wrong feature and other sources of rework) and value demand (new features/contracts/projects).&lt;/li&gt;
  &lt;li&gt;Study the variability and predictability of the failure demand. Only what is predictable is preventable. Take steps to prevent failure demand&lt;/li&gt;
  &lt;li&gt;Peoples behavior is a product of the system in which they work. If you want to change the people, change the system&lt;/li&gt;
  &lt;li&gt;Train people to handle the incoming requests and let them pull help on the things they don’t know how to solve.&lt;/li&gt;
  &lt;li&gt;Don’t standardize the work&lt;/li&gt;
  &lt;li&gt;Give the workers the means to control the work and the power to change it.&lt;/li&gt;
  &lt;li&gt;Measure the actual value delivered to customers.&lt;/li&gt;
  &lt;li&gt;The best way to learn counter-intuitive truths is to see and experience them for yourself&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goals are of course to deliver maximum value (the right features at the right time to solve the right problems) to customers in the shortest time frame possible. Sounds familiar? Read on for more in-depth discussion on each of the items in the list.&lt;/p&gt;

&lt;h3 id=&quot;get-knowledge&quot;&gt;Get Knowledge&lt;/h3&gt;

&lt;p&gt;This very much applies when you’re going to change a team or an organization, but how about the isolated act of writing software? To achieve a successful software project we have to &lt;em&gt;know what the customer wants and needs&lt;/em&gt; and more importantly &lt;em&gt;deliver that&lt;/em&gt;. So &lt;em&gt;study&lt;/em&gt; the clients needs and the client’s process and find out what they actually need. For, as many has experienced, what they say they need and what they actually need isn’t necessarily the same thing. And maybe Seddon’s idea of following the work is applicable to us as well? Actually manually following the work process one is trying the improve or eliminate with a software system. I don’t think we as an industry do this well enough.  And we must be prepared for the possibility that the answer might be “we don’t need the software system after all”. Scary for consultants I know, but what is more important: solving the problems of the client/company/society/world or making money?&lt;/p&gt;

&lt;p&gt;This also applies when trying to improve a software development process. Seddon advocates actually seeing and experiencing the flow of work in a process instead of merely performing a &lt;a href=&quot;http://en.wikipedia.org/wiki/Value_stream_mapping&quot;&gt;value stream mapping&lt;/a&gt; as kanban/lean advocates sometimes mention. Actually walk with the work item as it passes through the various stages of development and into production. And better yet, if the managers involved need some hard lessons - bring him/her along to see for themselves where the pain points lie. How can we change a development process if we don’t know how it actually works? So 1) start with studying the process you’re trying to change, follow the flow of work and then see what tools can solve the problems.&lt;/p&gt;

&lt;h3 id=&quot;study-demand&quot;&gt;Study Demand&lt;/h3&gt;

&lt;p&gt;I see failure demand as more or less all the stuff developers hate and frustrates them: bug reports, wrong features (not what I &lt;em&gt;really&lt;/em&gt; wanted etc. ), bad performance, instability etc. All sources of &lt;a href=&quot;http://www.thefreedictionary.com/rework&quot;&gt;re-work&lt;/a&gt;. To avoid re-work makes programmers happier and more productive as well as improve the steady flow of new features through the process. Value demand on the other hand are all forms of new work: feature requests, new contracts and other forms of new work. What is more fun: fixing your colleagues bugs ore creating new features? We all know the answer to that one, don’t we?&lt;/p&gt;

&lt;p&gt;And most important of all: identify the preventable forms of failure demand (is some classes of bug reports preventable by using a different testing strategy or development methodology? How can we change the architecture to avoid instability etc). Choose the right tools to prevent as much as possible of preventable failure demand and the value demand will increase by itself. Seddon’s point is that it is futile to try to maximize the value demand without dealing with the failure demand. You’re merely sub-optimizing. I’m not aware of any methodology that has a focus on this but I would like to hear from you if know.&lt;/p&gt;

&lt;h3 id=&quot;change-the-system&quot;&gt;Change The System&lt;/h3&gt;

&lt;p&gt;In Deming’s words, if you work on the people you work on the 10%, and if you work on the system, you work on the 90% as people’s behavior is a product of the system. In my mind this shows how important the right development methodology is for the productivity of the team and the people in it. But the methodology must suit the software your are developing, the risks involved and to a certain degree the people involved. Because programmers can be a tricky bunch :-) Systems Thinking also advocates putting the workers in charge of changing the work. This maps directly to the idea of self-organizing teams discussed in most agile methodologies like Scrum, XP and Kanban. I think this is an important idea and strict top-down management of development teams should be a thing of the past. And whatever methodology you pick as a &lt;em&gt;starting point&lt;/em&gt; for a project, never stop adapting it and improving it. Borrow and steal ideas, cheat or whatever improves the value you deliver.&lt;/p&gt;

&lt;h3 id=&quot;training-the-worker&quot;&gt;Training the Worker&lt;/h3&gt;

&lt;p&gt;Seddon’s arguments come from the front-office workers in service organizations, but it maps well to software development teams in my view. You need developers trained to handle the kind of work that the projects deals with, but not everyone needs to handle all aspects of it. This closely relates to the idea of cross-functional teams, collective ownership of the code and mentoring. Encourage developers to take on work items they don’t necessarily have the knowledge to solve, but institute a system of asking for, and getting help on it. The best way to learn is to work together with someone who knows how to solve the problem. Pair-programming is great in this regard and I would say the fastest way to spread knowledge in a team and educate newcomers. Collective ownership of the code is a bonus.&lt;/p&gt;

&lt;p&gt;Important in this regard is also the culture on the team. This is not something one can simply declare. This is culture that has to be created and nurtured from the ground up.&lt;/p&gt;

&lt;p&gt;The idea of software craftsmanship also comes to mind here, with established master-apprentice models in place for training new developers. I lighter version of this is a mentor/coach model working in the team with the new member and bringing that person up to speed. This transfers not only raw skill in the technology at hand but also the team culture and their way of doing things. A culture of learning is vital to a successful team in the long run and especially a team where people are replaced with some frequency.&lt;/p&gt;

&lt;p&gt;Another point is that programmer fresh out of college or university learns much faster by working with a mentor on a real-world project that working on made-up pet projects in the safe confines of a company. Because they learn the technical skills but also (and more importantly) the soft skills required when working as a programmer like talking to customers, discussing software design, formulating requirements (hey, sometimes clients can’t do this) and just behaving around a lot of different kind of people.&lt;/p&gt;

&lt;p&gt;The concept of kaizen (continuous improvement) is also a part of this. Create a culture where people and process is constantly scrutinized and improved. As managers in Toyota  (&lt;a href=&quot;http://en.wikipedia.org/wiki/Toyota_Production_System&quot;&gt;TPS&lt;/a&gt;) have experienced: if a process isn’t improving, it is degrading. And this in my view is true for technical skills and soft skills in people too.&lt;/p&gt;

&lt;h3 id=&quot;dont-standardize-the-work&quot;&gt;Don’t Standardize the Work&lt;/h3&gt;

&lt;p&gt;The closest analogy that comes to mind is coding standards, testing standards and the like and this is a tricky one. I don’t readily think this should be done with without good reasons. Some have argued that the need for coding standards, both for syntax and design, is greater when using dynamic languages like Ruby, Perl or Python, than with statically typed languages like C# or Java. This is primarily because the tools enforce much less and the need for discipline is greater. The need also increase with the size of the team.&lt;/p&gt;

&lt;p&gt;I’m not ready to agree fully with this, but I can see a valid point. It also is a factor of the skill of the team members and what they perceive as beautiful/clean code and what is dirty. One developer’s diamond can be another’s turd - simple as that. Another point is this: a team left to self-organize and dictate their own work and &lt;em&gt;not&lt;/em&gt; doing a good job at maintaining a clean code-base that supports re-factoring and addition of new features &lt;em&gt;need to be shown the path&lt;/em&gt; by someone who knows how. And notice the “shown the path” part, not “showed forward on the path”.&lt;/p&gt;

&lt;p&gt;I also think some fairly loose rules regarding “how we do it here” is worth having regardless of the technology involved. But the rules should come from the team, not from above. Company-wide standards can be very counter-productive especially when different technologies are involved in the projects.&lt;/p&gt;

&lt;h3 id=&quot;measure-the-value-delivered&quot;&gt;Measure the Value Delivered&lt;/h3&gt;

&lt;p&gt;Kai and Tom Gilb have in particular &lt;a href=&quot;http://gilb.com/blogpost112-7-truths-about-Agile-and-Scrum-that-people-don-t-want-to-hear-Part-1-of-7-Wrong-Focus-&quot;&gt;argued that agile methodologies aren’t focused on delivering value&lt;/a&gt; and measuring that value and I agree. Their &lt;a href=&quot;http://gilb.com/Project-Management&quot;&gt;Evo&lt;/a&gt; method for measuring business valyue address this. How can you know if you are actually delivering value to the customer when you’re not measuring it? Evo has a lot of tools for this and I think it can be useful in many situations. A real-world case presented by Kai Gilb at a recent Oslo XP meetup meeting concluded with more focused development efforts and more precise features delivered for the same amount of money compared to the clients earlier experience.&lt;/p&gt;

&lt;p&gt;The concrete task of measuring the quantifiable value you are delivering is a good idea and it’s the only metric actually being concerned about. Another important point from Seddon from service organizations is that you get the behavior you measure i.e. if you measure code coverage of the test suite, people will cheat to get a high coverage. And this does not mean good tests. This goes for all historical metrics that have been tried and failed in software development: lines-of-code produces, number of (unit) tests, work items etc.&lt;/p&gt;

&lt;p&gt;And as long as we’re on the topic: what is the actual value of estimation? There are some values in experiencing how long a task takes and you often can get a head-start on designing and seeing potential future problems when talking and thinking about a feature. But this a product of talking and thinking about a feature, not estimating it! In many organizations management want estimates. But aren’t they really after a commitment on what will be done at what time? Kanban practitioners often see this and use empirical evidence to determine how long something will take rather than predict it up front. A technique like a &lt;a href=&quot;http://open.bekk.no/2009/11/03/cumulative-flow-diagrams-with-google-spreadsheets/&quot;&gt;cumulative flow diagram&lt;/a&gt;  show you the time a work item take to clear the work process and if you have a fairly low variation in the size of the work items (you can control this) you use this to predict future deliveries.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;I think the current software methodologies have something the benefit from the ideas of Systems thinking, but as Seddon himself argues against Lean we can’t just take the ideas and run with them as they are primarily for a different domain (Lean for production and Systems thinking for public sector service organizations) but the we must adapt them to our field. As Seddon also advices when considering a tool:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Who invented the tool?&lt;/li&gt;
  &lt;li&gt;What problem was he trying to solve?&lt;/li&gt;
  &lt;li&gt;Do I have that problem?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Great advice in all walks of life.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Linkdropping on NoSQL, Lean and Systems Thinking</title>
   <link href="http://blog.knuthaugen.no//2010/01/linkdropping-on-nosql-lean-and-systems-thinking"/>
   <updated>2010-01-05T14:03:02+01:00</updated>
   <id>http://blog.knuthaugen.no//2010/01/linkdropping-on-nosql-lean-and-systems-thinking</id>
   <content type="html">&lt;p&gt;I thought I would round up a collection of links that interested me the past few weeks on various topics. First off: NoSQL databases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An almost impossibly detailed run-down of NoSQL databases: &lt;a href=&quot;http://www.vineetgupta.com/2010/01/nosql-databases-part-1-landscape.html&quot;&gt;http://www.vineetgupta.com/2010/01/nosql-databases-part-1-landscape.html&lt;/a&gt;. Part 2 should be good too.&lt;/li&gt;
  &lt;li&gt;Slides from a talk by Emil Eifrem of Neo4j on the different types of NoSQL databases: &lt;a href=&quot;http://www.slideshare.net/emileifrem/nosql-east-a-nosql-overview-and-the-benefits-of-graph-databases&quot;&gt;http://www.slideshare.net/emileifrem/nosql-east-a-nosql-overview-and-the-benefits-of-graph-databases&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update&lt;/strong&gt; Emil Eifrem mentions in his blog post in the comment another very nice overview of the &lt;a href=&quot;http://www.rackspacecloud.com/blog/2009/11/09/nosql-ecosystem/&quot;&gt;NoSQL ecosystem&lt;/a&gt; by Jonathan Ellis of Rackspace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then over to Lean and Kanban. Henrik Kniberg and Mattias Skarin just published a new book on InfoQ titled “&lt;a href=&quot;http://www.infoq.com/minibooks/kanban-scrum-minibook&quot;&gt;Kanban and Scrum - making the most of both&lt;/a&gt;”. If you’re not familiar with Henrik Kniberg’s work, I also suggest “&lt;a href=&quot;http://www.infoq.com/minibooks/scrum-xp-from-the-trenches&quot;&gt;Scrum and XP from the trenches&lt;/a&gt;”. Erling Wegger Linde’s “&lt;a href=&quot;http://erlingwl.wordpress.com/2009/12/03/a-kanban-brown-bag-recipe/&quot;&gt;A Kanban brown bag recipe&lt;/a&gt;” is also worth a read.&lt;/p&gt;

&lt;p&gt;And in the spirit of Lean: There is a video of talk by John Seddon of &lt;a href=&quot;http://www.systemsthinking.co.uk/home.asp&quot;&gt;Vanguard&lt;/a&gt; titled “&lt;a href=&quot;http://www.vimeo.com/4670102&quot;&gt;Cultural Change is Free&lt;/a&gt;”. Mainly about systems thinking in the public sector, but private sector aren’t infallible either. Seddon often criticize Lean for being wrong in many places, but I often feel he is criticizing a wrongful implementation of lean ideas, much the same as scrum is often criticized for the misgivings of wrongful implementations. Or rather he is criticizing the tool focus of  a lot of lean consultants not the lean principles themselves. And he stresses the differences between the Toyota Productions system and other kinds of organizations. You could also check out is talk “&lt;a href=&quot;http://www.infoq.com/presentations/rethinking-lean-service&quot;&gt;Re-thinking Lean service&lt;/a&gt;” on InfoQ which deals with the same topic in a slightly different packaging.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Posting to Movable Type From Emacs</title>
   <link href="http://blog.knuthaugen.no//2009/12/posting-to-movable-type-from-emacs"/>
   <updated>2009-12-29T16:55:56+01:00</updated>
   <id>http://blog.knuthaugen.no//2009/12/posting-to-movable-type-from-emacs</id>
   <content type="html">&lt;p&gt;I stumbled across
&lt;a href=&quot;http://www.emacswiki.org/emacs/download/weblogger.el&quot;&gt;weblogger.el&lt;/a&gt;
which is an xml-rpc interface for posting to several blog engines,
including Movable Type which I use. And I thought: Hey, why use that
somewhate crappy online editor for writing blog posts when one can use a fairly
OK operating system to do the job (aka Emacs).&lt;/p&gt;

&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;/h3&gt;
&lt;p&gt;Download weblogger.el from the above link and also the requisite &lt;a href=&quot;http://www.emacswiki.org/emacs/xml-rpc.el&quot;&gt;xml-rpc&lt;/a&gt; and load them in .emacs in the usual manner.&lt;/p&gt;

&lt;p&gt;Run weblogger-setup-weblog and fill in the correct values for configuring the blogging engine. For Movable Type the URL endpoint is http://your.blog.url/mt/mt-xmlrpc.cgi. The user name is your usual username, but the password is the special API password which can be found and set on the bottom of the user page in the admin interface. After that it’s just C-c C-n for a new entry. The setup will fetch all existing entries automatically so you can cycle through and edit too.&lt;/p&gt;

&lt;h3 id=&quot;caveats&quot;&gt;Caveats&lt;/h3&gt;
&lt;p&gt;It seems the current version of weblogger.el doesn’t handle drafts
properly against MT 4. The first version saved as a draft is published instantly :-/. Later saves keep the draft status. Also, the familiar C-c C-s for saving the buffer actually publishes. So you’re going live pretty soon.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A MacOS X Keyboard Layout for Programming</title>
   <link href="http://blog.knuthaugen.no//2009/12/a-macos-x-keyboard-layout-for-programming"/>
   <updated>2009-12-11T20:47:08+01:00</updated>
   <id>http://blog.knuthaugen.no//2009/12/a-macos-x-keyboard-layout-for-programming</id>
   <content type="html">&lt;p&gt;I have previously created my &lt;a href=&quot;http://blog.knuthaugen.no/2009/07/keyboard-layout-and-remapping-for-fun-and-profit.html&quot;&gt;own keyboard layout &lt;/a&gt; for windows and Linux in order to have a layout where the often-used keys in programming are more accessible than in the standard Norwegian layout. And when I recently got a (shiny) MacBook pro, I wanted to have the same keyboard layout for the Mac also. Consistency is key across machines, so the layout is the same as in the original progn keyboard layout (most braces and slashes available on home row or the row above). Downside is if decide to update it, I have to update three copies. Oh well…&lt;/p&gt;

&lt;p&gt;In the windows and Linux I ended up mapping caps-lock to alt-gr and binding a lot of new key combinations using that modifier. I chose a similar although slightly different model for the Mac. I choose to use the caps-lock key as another alt key (set in the keyboard system preference) and then map keys to alt+&lt;key&gt;for easier access. This means I loose the keys already bound on the alt key, but I keep the original keyboard layout easily accessible, and switching is a breeze. On the positive side, I don't mess with key bindings in either the Terminal app, Aquamacs (when fn is set to be meta) nor IntelliJ IDEA. Here's the finished new bindings:
&lt;img alt=&quot;keyboard.png&quot; src=&quot;../../../images/screenshot.png&quot; width=&quot;500&quot; height=&quot;247&quot; class=&quot;mt-image-center&quot; style=&quot;text-align: center; display: block; margin: 10px auto 20px;&quot; /&gt;&lt;br /&gt; Home row is the most important one, with often used characters, and the braces on homerow+1 on the right hand. Pointy brackets and pipe on the left hand.&lt;/key&gt;&lt;/p&gt;

&lt;h3 id=&quot;recipe&quot;&gt;Recipe&lt;/h3&gt;
&lt;p&gt;Here’s how I created the new keyboard layout for mac running 10.6 Snow Leopard (I think it’ll work on Leopard (10.5) as well, but haven’t tested it). Finding an existing layout in the .keylayout XML format was quite difficult but I found an &lt;a href=&quot;http://jardar.nvg.org/mac/tastatur/index.html&quot;&gt;extended Norwegian layout&lt;/a&gt; which had some often used keys already mapped nicely, like norwegian quation marksand some others. So I used that as a base.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;amp;id=ukelele&quot;&gt;Download Ukulele from the web page&lt;/a&gt; and install it.&lt;/li&gt;
  &lt;li&gt;Either start from scratch defining keys in Ukelele or start modifying an existing keyboard layout.&lt;/li&gt;
  &lt;li&gt;Open the character viewer (System preferences -&amp;gt; Language &amp;amp; Text -&amp;gt; Input Sources and select Character and keyboard viewer&lt;/li&gt;
  &lt;li&gt;Drag characters from the character viewer while holding down the desired modifier key.&lt;/li&gt;
  &lt;li&gt;Save under a suitable name. Be creative :-).&lt;/li&gt;
  &lt;li&gt;Copy to “/Library/Keyboard Layouts” for access for all users or “~/Library/Keyboard Layouts” for just your user.&lt;/li&gt;
  &lt;li&gt;log out and log in, and you should be able to choose the new keyboard layout from System Preferences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I called my keyboard layout progn, like the others I have created and &lt;a href=&quot;http://blog.knuthaugen.no/media/progn.keylayout&quot;&gt;you can download it&lt;/a&gt; if you want to use it for further tweaking. I made it available under the same creative commons license as this blog, so remixing is allowed provided you make the re-mixed versions available to the world wide intertubes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Doing do-release-upgrade on an offline ubuntu mirror</title>
   <link href="http://blog.knuthaugen.no//2009/12/doing-do-release-upgrade-on-an-offline-ubuntu-mirror"/>
   <updated>2009-12-03T13:42:13+01:00</updated>
   <id>http://blog.knuthaugen.no//2009/12/doing-do-release-upgrade-on-an-offline-ubuntu-mirror</id>
   <content type="html">&lt;p&gt;I have an offline mirror of the ubuntu package repositories for use on a network not connected to the internet. This works like a charm for updating packages on individual machines, but when it comes to doing a ‘do-release-upgrade’ to the next release, like these days I’m trying to upgrade to 9.10 Karmic Koala, we need some tricks to make this work. Firstly, sync your mirror so all the new packages of the new release are locally available. What you probably find is that the upgrade manager/do-release-upgrade software can’t seem to find a new available automagically. So how do we accomplish this?&lt;br /&gt;&lt;br /&gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;update-manager and do-release-upgrade reads the file /etc/update-manager/meta-release to find the location of the meta-release file. This points to the internet location changelogs.ubuntu.com normally. And if you just mirror the package repos, the meta-release file isn’t included. So we need to fetch it first: ‘wget http://changelogs.ubuntu.com/meta-release’. &lt;br /&gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Store it, for instance on the root of the internal mirror or some other convenient location, and put the url to it in the “URL” value in the /etc/update-manager/meta-release. If you’re upgrading to a LTS release, fetch the meta-release-lts file too and repeat the process.&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Edit the meta-release file you just downloaded and substitute the external mirror address with the url for the internal mirror so all package locations match up. For me this was replacing ‘archive.ubuntu.com’ with ‘explorer/mirror’ since the internal mirror is available at http://explorer/mirror/ubuntu/. Make sure the file is readable via http (or file permissions if using file access to repo). &lt;br /&gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Run update-manager or do-release-upgrade and the upgrade should work as you were using an internet mirror. &lt;br /&gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Hacking on munin 1.4</title>
   <link href="http://blog.knuthaugen.no//2009/11/hacking-on-munin-14"/>
   <updated>2009-11-27T20:17:46+01:00</updated>
   <id>http://blog.knuthaugen.no//2009/11/hacking-on-munin-14</id>
   <content type="html">&lt;p&gt;I have been doing the ol’ open source thing lately and helping out some old colleagues from Redpill-Linpro hacking on the much-awaited 1.4 version of &lt;a href=&quot;http://munin-monitoring.org&quot;&gt;munin&lt;/a&gt; (out today) which has tons of new features and not to mention tons of new plug-ins for just about all kinds of hardware and software out there. The &lt;a href=&quot;http://munin-monitoring.org/browser/tags/1.4.0/ChangeLog&quot;&gt;changelog&lt;/a&gt; is longer than most I’ve seen in a while, and that’s definitely a good thing. I’ve been doing mostly html+css work on 1.4 but I am planning to dig deeper into the core in the future and help out with some much needed re-factoring of the Perl code. And hopefully contribute some tests as well. It’s good to write Perl again.&lt;/p&gt;

&lt;h3 id=&quot;planned-plugins&quot;&gt;Planned plugins&lt;/h3&gt;
&lt;p&gt;I thought I work on some new plugins as well when I get spare time, namely&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SNMP plugin for Synology NAS&lt;/li&gt;
  &lt;li&gt;SNMP plugin for Qnap NAS&lt;/li&gt;
  &lt;li&gt;SNMP plugin for IBM Bladecenter H (if it has some good data available)&lt;/li&gt;
  &lt;li&gt;SNMP plugin for IBM Storage DS3000 series&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I also going to see if the NetApp at work spits out something more useful not covered in the existing netapp plugins.&lt;/p&gt;

&lt;p&gt;If you need monitoring and graphing of key metrics from your servers, network hardware, storage hardware and software servers (jmx plug-ins new in 1.4 too), you should give munin a spin.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Creating Craftsmen And a Craftsmanship company</title>
   <link href="http://blog.knuthaugen.no//2009/10/creating-craftsmen"/>
   <updated>2009-10-25T18:04:28+01:00</updated>
   <id>http://blog.knuthaugen.no//2009/10/creating-craftsmen</id>
   <content type="html">&lt;p&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;How does one go about creating software craftsmen in a company and more how does one create/transform a company to make this possible? These are the questions and thoughts I found myself with after attending an open space session at &lt;a href=&quot;http://smidig2009.no/&quot;&gt;Smidig 2009&lt;/a&gt; (the Norwegian Agile conf). &lt;br /&gt;&lt;br /&gt;Perhaps the most known craftsmanship consulting firms are Obtiva and 8th Light, both located in Illinois USA (one on Chicago and the other in Round Lake). They are, as far as I know, created specifically to a have a master and apprentice model and takes this to extremes. Their &lt;a href=&quot;http://obtiva.com/news/2009/06/15/06-09-obtiva-and-8th-light-swap/&quot;&gt;craftsman exchange&lt;/a&gt; is a fine example of this, where senior consultants got to piggyback the other firm's people on real world projects for a week, just to learn from them on how they build software and how they run their company. They also hire programmers with the title of apprentice and one account of being an apprentice at 8th Light is found in this blog. Not bad to commit JRuby code while formally being and apprentice. I especially noticed the part about being specific (coding) challenges and the getting feedback from not just one but all masters in the firm. I suspect the amount of condensed learning is formidable. But does this approach work when you trying to transform a &quot;normal&quot; consultancy firm into something more craftsmanship friendly?&lt;br /&gt;&lt;br /&gt;Let's examine some other tools to use, more tuned in to (Norwegian) consulting firms and companies that do not have this in place now, but rather have their hands full with competing and hanging on to clients. What should one do?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;Organic interest groups&lt;/font&gt;&lt;/b&gt;&lt;br /&gt;Mike Cohn explained in Oslo XP meetup talk recently Google's model of special groups within the company forming horizontally across projects. &lt;a href=&quot;http://softwareapprenticeship.wordpress.com/&quot;&gt;A talk&lt;/a&gt; on the same subject at JavaZone 2009 in Oslo discussed the same idea employed in &lt;a href=&quot;http://www.knowit.no/&quot;&gt;KnowIT ObjectNet&lt;/a&gt;, a consulting firm in Oslo, Norway. They even have a cooperation with the Simula Research center on finding out what actually works best.The important bits are:&lt;br /&gt;&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Voluntary membership based on interest (Google, KnowIt)&lt;br /&gt;&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Each person can join many groups (KnowIt)&lt;br /&gt;&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Organic growth bottom-up is preferred, but it should be allowed to transform into more formal groups over time. (Cohn)&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Given a mandate to change practices (google, KnowIt)&lt;/font&gt;&lt;/li&gt;&lt;li&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Communicate the results&lt;br /&gt;&lt;/font&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;font style=&quot;font-size: 1em;&quot;&gt;This is a key issue. People need to have a forum to evolve their skills outside of the current customer project, which in many cases isn't well suited for that, deadlines and all. Learning a new language, which in my mind brings with it a lot of benefits especially when learning one from another paradigm than one is used to, is best done outside the creation of enterprise code under time pressure. Besides, one needs room to experiment and fail. Demanding some kind of output from the work is important to keep it in check and make the group prioritize and work toward a goal, not to mention to disseminate the knowledge gained to non-members and even the development community at large. &lt;br /&gt;&lt;br /&gt;Reports, internal or external talks or patterns &amp;amp; practises changes are some types of outputs. In the concrete case of KnowIt, the groups were allowed to bring in outsiders which in one case created a cooperation with the Norwegian Scala user group ScalaBin. A good example of an activity that is of value to the entire community, not just the company itself. And I would argue that a good software development community is an important factor in creating good companies and good developers.&lt;br /&gt;&lt;br /&gt;This should work well for consultancies in my opinion. People need some time off the project to keep in touch with the company and what better way than to spend it doing research on a hot topic that may bring in future clients? KnowIt also found that you need some hot topics to keep the juices flowing in the developers, but the company could put more regular topics to good use to. And it all depends on the people you have on board. &lt;br /&gt;&lt;br /&gt;&lt;/font&gt;
&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;b&gt;Coaching&lt;br /&gt;&lt;/b&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;A new programmer fresh out of college, needs guidance to avoid stepping into balls of mud and re-inventing the wrong wheels. How do we accomplish this? Several thoughts emerged on the open space at smidig 2009. Pair programming&lt;/font&gt;&lt;/font&gt;
&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt; is an excellent way of transfering knowledge from one head to another. There are a number of factors that &lt;a href=&quot;http://blog.obiefernandez.com/content/2009/09/10-reasons-pair-programming-is-not-for-the-masses.html&quot;&gt;need to be in place&lt;/a&gt; for it to function properly, but done right can produce great results. For a consultancy it requires that a customer allows junior programmers to be put on the project team and the (at least perceived overhead) of mentoring them.&lt;br /&gt;&lt;br /&gt;Concrete feedback and guidance while working on real-world problems was mentioned by several people and the rationale was that learning isn't as rapid or valuable in constructed problems. You need wet feet to really appreciate the boots you're getting. Code reviews can also be used to make novice programmers see the errors of their ways &lt;i&gt;and&lt;/i&gt; seeing how it should have been done.&lt;br /&gt;&lt;br /&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;b&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;Quality&lt;/font&gt;&lt;/b&gt;&lt;/font&gt;&lt;br /&gt;&lt;/font&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Never compromise on quality. If a customer ask you to deliver the same features, only a little bit faster, say no. Plain old no. If you're doing it faster, you're cutting corners. You may not think you are, but you are. This requires backbone from both developers in the trenches and managers negotiating contracts.&amp;nbsp;&amp;nbsp; &lt;br /&gt;&lt;br /&gt;Don't do overtime (at least not as a rule). You need sustainable pace. Burned-out developers make poor craftsmen and they need a life outside work. Besides, who has the energy to learn Clojure or contribute on open source projects if they spend all their time and energy on work? You need time off. Use it&lt;/font&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;.&lt;b&gt;&lt;br /&gt;&lt;br /&gt;Learning and teaching&lt;br /&gt;&lt;/b&gt;&lt;/font&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;At the core of it all is learning. Becoming a craftsman requires lifelong learning.The principle of Kaizen (from Lean) embodies what I think is the right path: small incremental learning each and every day, improving a small part of the workplace, your skills or your team. Big steps are much harder to do and get right.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;Some other points regarding learning: some for the developer and some are for the company.&lt;font style=&quot;font-size: 0.8em;&quot;&gt; &lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;
&lt;ul&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;li&gt;Realise how little you know and be open to learn from everyone you meet. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Always challenge your beliefs.&lt;/li&gt;&lt;li&gt;Always buy the books you want (and read them)&lt;/li&gt;&lt;li&gt;Keep up with the trends&lt;/li&gt;&lt;li&gt;Seek new challenges&lt;/li&gt;&lt;/font&gt;&lt;/font&gt;&lt;/ul&gt;
&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;For the company:&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;
&lt;ul&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;li&gt;Allow consultants the time to develop &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Nurture a culture of learning and improve the company in small increments, not just the people. Kaizen all the way.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Allow for mistakes and but ensure learning from them, both individually and as a group.&lt;/li&gt;&lt;/font&gt;&lt;/font&gt;&lt;/ul&gt;
&lt;font style=&quot;font-size: 1.5625em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;And remember: you can't expect to change other people, you can only change yourself.&lt;br /&gt;&lt;br /&gt;A craftsman shares his knowledge with others and tries to make the community around him grow with him. Give talks at conferences, user groups or inside you company. As a company, encourage developers to give talks, give them time and resources to do so. Follow up on it. Spread the word. Nothing forces you to know your stuff quite like giving a talk on it to an audience. Scary but rewarding.&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;&lt;b&gt;Tools&lt;br /&gt;&lt;/b&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;A craftsman needs to know the tools of the trade and to use the right tool for the right job. This is mainly a skill one gets through experience. But the right kind of experience - a varied one. This is the consultants world and consultants are in better shape than most in-house developers when it comes to this. But learning a new paradigm or language will take you far in my opinion. Just the act of learning a totally different language, even though you just use if for toy projects, refreshes your perspectives on your &quot;day-job&quot; language. Especially if your job language is C# or java and you learn a dynamic language or a functional one. The perspective you attack problems with in your job language will change and change for the better. &lt;br /&gt;&lt;br /&gt;Knowing your IDE or other developer tools is essential, as is knowing and tweaking you platform. This means also that companies must let developers have full control over their development machine. The opposite actually still occurs. And not to mention: the developers need the sexiest hardware money can buy and plenty of screen real-estate.&lt;br /&gt;&lt;br /&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;b&gt;People skills&lt;br /&gt;&lt;/b&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;A craftsman need people skills. She needs to understand the customer and make the customer understand her. She needs to be a team player and know when to shut up and when to speak up. She must be responsible, honest and brave. How to achieve all this? Can introvert geeks manage these things? Well, some people will have to work more on it than others, but I feel the environment we operate in and the feedback we get, can develop many of the skills. Keywords are giving developers responsibility to make technical decisions, to work independently, embrace, encourage and reward team efforts, not individual efforts. Challenge their fears and push the limits of their comfort zone a bit, within reason. Careful not to push to hard :-)&lt;br /&gt;&lt;br /&gt;And besides: The primary output of a software project is people, not customer value and software (paraphrasing Ward Cunningham). You are the sum of the work laid down. As an ending note: the book &lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;&lt;/font&gt;
&lt;p&gt;&lt;a href=&quot;http://oreilly.com/catalog/9780596518387/&quot;&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Apprenticeship Patterns&lt;/font&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;Guidance for the Aspiring Software Craftsman&lt;/font&gt;&lt;/a&gt; by&lt;span rel=&quot;dc:creator&quot;&gt;&lt;span&gt;&lt;a about=&quot;urn:x-domain:oreilly.com:agent:pdb:2736&quot; class=&quot;authorname&quot; href=&quot;http://www.oreillynet.com/pub/au/3357&quot; property=&quot;foaf:name&quot; rel=&quot;foaf:homepage&quot; typeof=&quot;foaf:Person&quot;&gt; Dave Hoover&lt;/a&gt; and &lt;a about=&quot;urn:x-domain:oreilly.com:agent:pdb:3202&quot; class=&quot;authorname&quot; href=&quot;http://www.oreillynet.com/pub/au/3421&quot; property=&quot;foaf:name&quot; rel=&quot;foaf:homepage&quot; typeof=&quot;foaf:Person&quot;&gt;Adewale Oshineye&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; &lt;font style=&quot;font-size: 1.5625em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;font style=&quot;font-size: 1em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;&lt;font style=&quot;font-size: 1.25em;&quot;&gt;&lt;font style=&quot;font-size: 0.8em;&quot;&gt;is out on O'Reilly now and discuss a lot of this in depth (although I haven't read it yet). &lt;br /&gt;&lt;br /&gt;This blog post is getting to long as it is. Feel free to continue in the comments if you have something to add. &lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&amp;nbsp;&lt;/font&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/font&gt;&lt;br /&gt;&lt;/font&gt;&lt;/font&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>A SyntaxHighlighter brush for Lisp</title>
   <link href="http://blog.knuthaugen.no//2009/10/a-syntaxhighlighter-brush-for-lisp"/>
   <updated>2009-10-14T21:13:31+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/10/a-syntaxhighlighter-brush-for-lisp</id>
   <content type="html">&lt;p&gt;I use &lt;a href=&quot;http://alexgorbatchev.com/wiki/SyntaxHighlighter&quot;&gt;SyntaxHighlighter&lt;/a&gt; for highlighting code snippets on this blog and I found out that it didn’t come with a brush for the 2.0 version highlighting lisp code. So I wrote my own. I found a SyntaxHighlighter 1.5 brush &lt;a href=&quot;http://han9kin.doesntexist.com/22&quot;&gt;through google&lt;/a&gt; and I converted that to a 2.0 brush and it’s now in a state that sort of works. At least it highlights some code :-) I used that as an excuse to try out qunit as well and write some unit/integration tests for the code. &lt;a href=&quot;http://github.com/jquery/qunit&quot;&gt;Qunit&lt;/a&gt; is cool. There’s not much code in the brush itself as it leans heavily on the core SyntaxHighlighter stuff. So the tests are testing the framework also. But hey, I’m not picky.&lt;br /&gt;&lt;br /&gt;It’s all to be found on &lt;a href=&quot;http://github.com/knuthaug/sh-lisp&quot;&gt;github:knuthaug/sh-lisp&lt;/a&gt;.Patches/pulls via github is more than welcome. I plan to dig out my copy of “Ansi common Lisp” and go through the lists of functions, keywords and macros a bit more in detail. Some more tests are in order to. One question which comes to mind is if it’s best to do one that does common lisp and emacs-lisp, or one brush for each dialect?&lt;br /&gt;&lt;br /&gt;Here’s the code for brush:&lt;br /&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Lisp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;funcs&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lambda list progn mapcar car cdr reverse member append format&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;keywords&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;let while unless cond if eq t nil defvar dotimes setf listp numberp not equal&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;macros&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;loop when dolist dotimes defun&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;operators&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;gt; &amp;lt; + - = * / %&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;regexList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;regexLib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;doubleQuotedString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;w+;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;plain&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;;.*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;comments&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;w|-)+&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;variable&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getKeywords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keywords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;gm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getKeywords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;macros&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;gm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getKeywords&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;funcs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;gm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;functions&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Lisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prototype&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Highlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;brushes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Lisp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aliases&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;lisp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content>
 </entry>
 
 <entry>
   <title>Definition of an Agile Leader</title>
   <link href="http://blog.knuthaugen.no//2009/09/definition-of-an-agile-leader"/>
   <updated>2009-09-30T20:59:54+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/09/definition-of-an-agile-leader</id>
   <content type="html">&lt;p&gt;Patrick Wilson Welsh blogged a while back about &lt;a href=&quot;http://patrickwilsonwelsh.com/?p=170&quot;&gt;the role of the agile leader&lt;/a&gt; and I think he is pretty bang on. He outlines four qualities or tools an agile leader needs to be successful in an environment where the old maxims of project management, or management in general, or even technical leadership doesn’t cut it anymore. The tools are:&lt;br /&gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;Continous team building&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Continous planning&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Continous unblocking&lt;br /&gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;Continous improvement (which in the lean tradition is also known as &lt;a href=&quot;http://en.wikipedia.org/wiki/Kaizen&quot;&gt;Kaizen)&lt;/a&gt;.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;The importance of each one varies with the environment in which a team operates, but all are necessary. And one of the key things a lot of organisations doesn’t seem to understand is that building a team isn’t about creating a team that works well with a benevolent dictator keeping a watchful eye on them at all times, but rather creating teams that to almost no degree &lt;u&gt;needs&lt;/u&gt; a “boss” at all. This is a team where continous self-improvement for the individuals and the team as a whole isn’t something mysterious regarding japanese logographics, but rather routine. The thing a team really needs help with is unblocking. This must be done by someone who shouldn’t write code at the same time. I’ve tried that model and it sucks royally.  &lt;br /&gt;&lt;br /&gt;But how do we create or foster leaders like this? There are several things we need. We need whole organisations that are willing and easy to change. No small feat. Is this best achieved from the bottom up or top down? The lean/toyota philosophy dictates bottom up or rather at all levels in the organisation and that all levels are of equal importance. A common trait in companies and organisations is that rigidity comes with size. And a rigid already established company is even harder to change that creating lean smaller companies. &lt;br /&gt;&lt;br /&gt;A team with such a leader wouldn’t function very well in an old-school environment, and they must go hand in hand. Educational institutions must somehow adress these challenges in the future as they’re at least not in Norway even adressing agile methodologies properly. They’re way behind the curve as it is. And &lt;a href=&quot;http://www.thehackerchickblog.com/2009/09/agile-leadership-methodology-ain-enough.html&quot;&gt;methodologies aren’t enough&lt;/a&gt; as Abby Fichtner wrote in a blog post which was a precursor to Patrick’s. Some of these values or tools mentioned by Abby and Patrick are mentioned in several methodologies but perhaps not the continous part in all settings. And do you really need a full blown methodology that dishes out all this and gives you a certification for just showing up at a course? I don’t think so.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A screenscraping filesystem for accessing media archive</title>
   <link href="http://blog.knuthaugen.no//2009/09/a-screenscraping-filesystem-for-accessing-media-archive"/>
   <updated>2009-09-18T10:48:07+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/09/a-screenscraping-filesystem-for-accessing-media-archive</id>
   <content type="html">&lt;p&gt;This is probably directly interesting to Norwegians since the content in question is in Norwegian, but the concept is so very cool. In Norway we have &lt;a href=&quot;http://www.nrk.no/&quot;&gt;NRK&lt;/a&gt; the state broadcast corporation which has a very contemporary view on sharing its on content to the public. It is after all paid for by the people through taxes. And they have a rich library of media streams available on the net of mostly all television productions they created themselves.&lt;/p&gt;

&lt;p&gt;I came across &lt;a href=&quot;http://blog.averlend.com/2009/05/23/nrk-som-filsystem/&quot;&gt;this little blogpost&lt;/a&gt; (in Norwegian) about Erlend Klakegg Bergheim’s effort to write &lt;a href=&quot;http://fuse.sourceforge.net/&quot;&gt;a fuse filesystem&lt;/a&gt; which scrapes the archive web pages and creates a file system which you can mount as a regular file system on linux. Then just point your asx-capable media player (like vlc, mplayer, totem or kaffeine) to the “file” and play away. It is simply genius and ingeniously simple and makes browsing the content a breeze.&lt;/p&gt;

&lt;p&gt;It’s written in python and chimes in at a 250 lines of code in total. The source is available at &lt;a href=&quot;http://github.com/klakegg/nrkfs&quot;&gt;github:klakegg/nrkfs&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The shunting yard algorithm kata</title>
   <link href="http://blog.knuthaugen.no//2009/09/the-shunting-yard-algorithm-kata"/>
   <updated>2009-09-15T10:21:26+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/09/the-shunting-yard-algorithm-kata</id>
   <content type="html">&lt;p&gt;After attending a &lt;a title=&quot;Code dojo randori&quot; href=&quot;http://dojo.wikidot.com/randori&quot;&gt;code dojo randori&lt;/a&gt; with the &lt;a title=&quot;Oslo XP meetup&quot; href=&quot;http://xp.meetup.com/13/&quot;&gt;Oslo XP meetup&lt;/a&gt; where we worked our way about half-way through the kata for the &lt;a title=&quot;Shunting yard algorithm&quot; href=&quot;http://en.wikipedia.org/wiki/Shunting_yard_algorithm&quot;&gt;Shunting yard algorithm&lt;/a&gt;, I decided to have a go at finishing it completely in Java (which I am quite rusty at) and the trying it in Ruby (at which I am a total newbie) and examine the differences. Especially Javas somewhat sketchy regexp support was something that annoyed me, and Ruby excels at that part.&lt;/p&gt;

&lt;p&gt;The Shunting yad algorithm is an algorithm for transforming infix notation to reverse polish notation, often used for arithmetic expressions and takes operator precedence and associativity into account.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My book list</title>
   <link href="http://blog.knuthaugen.no//2009/09/my-book-list"/>
   <updated>2009-09-15T10:08:32+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/09/my-book-list</id>
   <content type="html">&lt;p&gt;This is a list of all the books I’ve read that are in some way related to programming and software development, along with some thoughts on them.&lt;/p&gt;
&lt;h2&gt;Languages&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Ruby-Programming-Language-David-Flanagan/dp/0596516177&quot;&gt;The Ruby Programming Language&lt;/a&gt; by David Flanagan and Yukihiro Matsumoto. Really a comprehensive guide to Ruby 1.8 and Ruby 1.9, but not a &quot;how-to program&quot; type of book. Simply wonderful information density and one of the clearest programming books I've read in a long time.&lt;/li&gt;
	&lt;li&gt;Programming C# 3.0&lt;/li&gt;
	&lt;li&gt;C# 3.0 Cookbook&lt;/li&gt;
	&lt;li&gt;C# 3.0 Design patterns by Judith Bishop. A nice overview of common design patterns and how best to implement them in C#.&lt;/li&gt;
	&lt;li&gt;Programming Perl. A must when beginning to program Perl. Nice reference and enough depth to keep you going for a long time.&lt;/li&gt;
	&lt;li&gt;Practical mod_perl&lt;/li&gt;
	&lt;li&gt;Perl Best Practices by Damian Conway&lt;/li&gt;
	&lt;li&gt;Higher-order Perl by Mark Jason Dominus. This is a gem of a book for every Perl developer that wishes to expand her repertoire of tricks, but not suitable for beginners. It tackles topics such as memoization, currying, recursion and iterators, recursive-descent parsers and other neat stuff for transforming code using code.&lt;/li&gt;
	&lt;li&gt;Perl Cookbook. A collection of recipes for solving small and large problems in idiomatic Perl.&lt;/li&gt;
	&lt;li&gt;Perl Testing by Ian langworth and Chromatic. Nice rundown of Perl testing covering both Test::Class and Test::More. It's in the developer notebooks series so it's really hands on all the way through.&lt;/li&gt;
	&lt;li&gt;Javascript: The Definite Guide.&lt;/li&gt; 
       &lt;li&gt;Javascript: the good parts by Douglas Crockford. What is says on the cover. Really.&lt;/li&gt;
	&lt;li&gt;Ansi Common Lisp by Paul Graham. I would say this book can be compared to Kernigan &amp;amp; Ritchie's The C Programming Language. More parenthesis, though.&lt;/li&gt;
	&lt;li&gt;XSLT 2.0 by Michael Kay. Handy reference for those times when there's no way to escape XSLT. &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Methodology&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Agile Software Development with Scrum By Ken Schwaber&lt;/li&gt;
	&lt;li&gt;&lt;a title=&quot;amazon: Agile estimating and planning&quot; href=&quot;http://www.amazon.co.uk/Agile-Estimating-Planning-Robert-Martin/dp/0131479415/&quot;&gt;Agile Estimating and planning&lt;/a&gt; by Mike Cohn. A very nice book covering a lot of nooks and crannies regarding estimating and planning agile software development. I would say a must for all people involved in agile projects.&lt;/li&gt;
	&lt;li&gt;Agile Software development by Alistair Cockburn. One of the best discussions on agile software development as a field, without stranding on specific methodologies. A very good read, especially the parts on communication and individuals.&lt;/li&gt;
	&lt;li&gt;Test-driven Development by Dave Astels&lt;/li&gt;
	&lt;li&gt;The Pragmatic Programmer by Dave Thomas and Andy Hunt&lt;/li&gt;
	&lt;li&gt;Continuous Integration by Paul M. Duvall.  Didn't tell me much I didn't already know, but gives a nice overview of continous integration for newbies in the field. &lt;/li&gt;
	&lt;li&gt;Pragmatic Project Automation by Mike Clark. Quite basic rundown of the task of automating all (as many as possible) tasks in building a software project. Beginner-level. &lt;/li&gt;
	&lt;li&gt;Xunit Test Patterns by Gerard Meszaros. High-level discussion of test patterns for development. Testing in a TDD/BDD sense. &lt;/li&gt;
	&lt;li&gt;Junit Recipes by J.B. Rainsberger&lt;/li&gt;
	&lt;li&gt;Clean Code by Robert C. Martin.&lt;/li&gt;
      &lt;li&gt;Scrumban by Corey Ladas.&lt;/li&gt;
        &lt;li&gt;Code Complete 2nd edition by Steve McConnel. A bible on software development in many ways. Packed with useful advice even though a lot is common knowledge if you been around for a few years. &lt;/li&gt;
 &lt;li&gt;Scrum and Kanban - making the most of both by Henrik Kniberg and Mattias Skarin&lt;/li&gt;
&lt;li&gt;Kanban - Successful Evolutionary Change for Technology Organizations by David Anderson&lt;/li&gt;
	&lt;li&gt;Extreme Programming explained by Kent Beck&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/Software-Development-Principles-Patterns-Practices/dp/0135974445/ref=sr_1_3?ie=UTF8&amp;amp;s=books&amp;amp;qid=1248250805&amp;amp;sr=8-3&quot;&gt;Agile Software Development, Principles, Patterns, and Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Design and architecture&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Domain-driven Design by Eric Evans&lt;/li&gt;
	&lt;li&gt;Refactoring by Martin Fowler&lt;/li&gt;
       &lt;li&gt;Code Complete 2nd ed. by Steve McConnell&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Machine learning&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Data mining by Ian H. Witten and Eibe Frank&lt;/li&gt;
	&lt;li&gt;Data Mining - Concepts and Techniques by Jiawei Han and Micheline Kamber&lt;/li&gt;
	&lt;li&gt;Pattern Classification by Duda, Hart and Stork&lt;/li&gt;
	&lt;li&gt;Pattern Recognition and Machine Learning by Christopher M. Bishop&lt;/li&gt;
	&lt;li&gt;Fundamentals of Neural Networks by Laurene Fausett&lt;/li&gt;
	&lt;li&gt;Neural Networks for Pattern Recognition by Christopher M. Bishop&lt;/li&gt;
	&lt;li&gt;Fuzzy Modeling and Genetic Algorithms for Data Mining and Exploration by Earl Coz&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Applied Cryptography by Bruce Schneier. If you don't know who Bruce Schneier is, take a look at &lt;a href=&quot;http://www.schneierfacts.com/&quot;&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Wish list/future reading&lt;/h2&gt;
&lt;ul&gt;
	
&lt;li&gt;Leading Lean Software Development: Results Are not the Point by Mary and Tom Poppendieck.&lt;/li&gt;
&lt;li&gt;Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman
By Dave Hoover, Adewale Oshineye&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.amazon.co.uk/gp/product/0596527675/&quot;&gt;The Art of Agile Development&lt;/a&gt; by James Shore and Chromatic&lt;/li&gt;
&lt;li&gt;Working Effectively With Legacy Code by Michael Feathers.&lt;/li&gt;
 &lt;li&gt;Software Craftsmanship by Pete McBreen&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>About the Author</title>
   <link href="http://blog.knuthaugen.no//2009/09/about-me"/>
   <updated>2009-09-15T10:07:46+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/09/about-me</id>
   <content type="html">&lt;p&gt;I am Knut Haugen [Knu:t Hæugen], a software developer working in Oslo, Norway. I am programming on and off in Perl, Ruby, C#, PHP and Java and I have even had my hands on Lisp. I prefer to use a Linux environment whenever I can, but I’m forced to work on windows now and again. Ooh, and I recently got a mac (shiny!) and I love it.  Other interests in the field of software development includes methodologies like almost everything that falls within the agile sphere, and scrum and lean/kanban in particular. I’m also partial to TDD/BDD/DDD techniques but I still consider myself in the learning stage at this.&lt;br /&gt;&lt;br /&gt;I got started with computers one summer vacation when I was around 14 or so, when my uncle (who got started with computers back in the 70s) had me tag along when he programmed a horse-betting game on a casio calculator in BASIC. And then I generally didn’t catch on besides playing Doom and 3D Wolfenstein on my dads 386. I was more into photography and sports and worked as a freelance photographer from the age of 15 to 20. I applied for photojournalist school, but didn’t get in. My second choice was computer science and I ended up there. I bought my first own computer before starting the course and the night  before I moved my uncle (yes the same uncle) came by with a stack of cds and said “install these first then windows”). The cd’s where FreeBSD 2.2.6 and I can’t think of a deeper pool to jump into when fairly new to computers.&lt;br /&gt;&lt;br /&gt;My first programing language was VB5 as that was the language the course “introduction to programming” was taught in. I luckily moved on to Java (eh, not so lucky after all, but better than VB) and then Perl while in school and got stuck mostly doing Perl for the first seven years of my career as a consultant. Nowadays it’s mostly Ruby, C# and PHP and I can see a java project on the horizon. If I can choose freely I would prefer working in Perl (which I still know best) or Ruby. In fact any dynamic language is better than a statically typed one. &lt;br /&gt;&lt;br /&gt;Some more links:&lt;br /&gt;&lt;a href=&quot;http://twitter.com/knuthaug&quot;&gt;http://twitter.com/knuthaug&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://github.com/knuthaug&quot;&gt;http://github.com/knuthaug&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://no.linkedin.com/in/knuthaugen&quot;&gt;http://no.linkedin.com/in/knuthaugen&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;http://www.flickr.com/photos/megafon/&quot;&gt;http://www.flickr.com/photos/megafon/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My developer toolchain for Ruby</title>
   <link href="http://blog.knuthaugen.no//2009/08/my-developer-toolchain-for-ruby"/>
   <updated>2009-08-20T15:49:15+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/08/my-developer-toolchain-for-ruby</id>
   <content type="html">&lt;p&gt;I recently learned &lt;a href=&quot;http://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt; and I thought I’d lay out the different tools I use when programming Ruby just for self documentation purposes. Most weight is put on the IDE and the TDD tools that I use.&lt;/p&gt;
&lt;h3&gt;IDE&lt;/h3&gt;
&lt;p&gt;IDE is a big deal for many people and I have tried two, Eclipse and Emacs. Since I normally only program Ruby on Linux, these are my two alternatives. In Eclipse I’ve tried the ruby plugin from Aptana and the standard one in the “Programming languages”  section of the eclipse update manager. and I can’t say I prefer one over the other. Since Ruby is a dynamic language you won’t get the benefit of refactoring and completion like you would in Java, and this has lead me to ditch Eclipse for Ruby in favor of Emacs. What I miss in Emacs is integrated docs and integrated test runner. It’s possible in emacs as well but takes some hacking. We’ll see if I get around to that. I’ve been using Emacs for 12 years or so, and the first tool you pick always stick. This is my current ruby setup for Emacs:&lt;/p&gt;
&lt;pre class=&quot;brush: lisp&quot;&gt;
(autoload 'yaml-mode &quot;yaml-mode&quot; &quot;&quot; t)
(autoload 'ruby-mode &quot;ruby-mode&quot; &quot;&quot; t)
(autoload 'rhtml-mode &quot;rhtml-mode&quot; &quot;&quot; t)

(require 'flymake-ruby)
(add-hook 'ruby-mode-hook 'flymake-ruby-load)

(add-hook 'ruby-mode-hook
          (lambda()
            (add-hook 'local-write-file-hooks
                      '(lambda()
                         (save-excursion
                           (untabify (point-min) (point-max))
                           (delete-trailing-whitespace)
                           )))
            (set (make-local-variable 'indent-tabs-mode) 'nil)
            (set (make-local-variable 'tab-width) 2)
            ;;(imenu-add-to-menubar &quot;IMENU&quot;)
            (local-unset-ket &quot;TAB&quot;)
            ;;(define-key ruby-mode-map &quot;TAB&quot; 'yas/expand)
            (define-key ruby-mode-map &quot;\C-m&quot; 'newline-and-indent)
            (require 'ruby-electric)
            (ruby-electric-mode t)
            ))

;(add-hook 'ruby-mode-hook
;          '(lambda ()
;             (make-variable-buffer-local 'yas/trigger-key)
;             (setq yas/trigger-key [tab])))

(provide 'ruby)
&lt;/pre&gt;
&lt;p&gt;I use flymake to do interactive compile as I type but I suspect I’ll be turning this off when Ruby gets more into my fingers.&lt;/p&gt;

&lt;p&gt;I want to get irb up and running from within Emacs but haven’t gotten that far yet. In addition to the ruby specific stuff I use &lt;a href=&quot;http://ecb.sourceforge.net/&quot;&gt;ECB&lt;/a&gt;, &lt;a href=&quot;http://www.emacswiki.org/emacs/AutoComplete&quot;&gt;auto-complete&lt;/a&gt; and &lt;a href=&quot;http://code.google.com/p/yasnippet/&quot;&gt;yasnippet&lt;/a&gt; for extra support. In fact my complete emacs setup is available at the &lt;a href=&quot;http://github.com/knuthaug/emacs/tree/master&quot;&gt;emacs github repository&lt;/a&gt;. I’m also thinking about getting some elisp together to be able to integrate the testrunner into a shell window in emacs. The shell part isn’t hard but it takes some configuring to get the window electric and behaving the way I want.&lt;/p&gt;
&lt;h2&gt;TDD/BDD tools&lt;/h2&gt;
&lt;p&gt;I develop in a TDD/BDD style and most of the tools are geared towards that. Here’s a list:&lt;/p&gt;
&lt;ul&gt;
        &lt;li&gt;The ruby &lt;a href=&quot;http://metric-fu.rubyforge.org/&quot;&gt;metric_fu&lt;/a&gt; for all your analysis and metric needs. It wraps a lot of other rubygems that are handy. It covers code coverage with &lt;a href=&quot;http://eigenclass.org/hiki/rcov&quot;&gt;rcov&lt;/a&gt;, cyclomatic complexity with &lt;a href=&quot;http://saikuro.rubyforge.org/&quot;&gt;Saikuro&lt;/a&gt;, &lt;a href=&quot;http://ruby.sadi.st/Flay.html&quot;&gt;Flay&lt;/a&gt; for duplication detection, &lt;a href=&quot;http://ruby.sadi.st/Flog.html&quot;&gt;Flog&lt;/a&gt; for misc. pain points, &lt;a href=&quot;http://github.com/kevinrutherford/reek/tree/master&quot;&gt;reek&lt;/a&gt; for code smells and more. Nice graphs and all.&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;http://cukes.info/&quot;&gt;Cucumber&lt;/a&gt; for BDD style outside-in testing. Does a great job both for web applications and non-web applications.&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html&quot;&gt;Test::Unit&lt;/a&gt; for low-level unit test for driving out the details of classes.&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;http://rspec.info/&quot;&gt;RSpec&lt;/a&gt; for the times when the Test::Unit assertions feels awkward.&lt;/li&gt;
        &lt;li&gt;&lt;a href=&quot;http://github.com/&quot;&gt;Github&lt;/a&gt; for storing everything. Git is the coolest version control system since, well, since ever.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally things move &lt;em&gt;so&lt;/em&gt; damn fast in the ruby space and it’s hard to keep up when it’s not your main activity.&lt;/p&gt;
&lt;h2&gt;What's next&lt;/h2&gt;
&lt;p&gt;I haven’t tried Ruby-on-Rails yet and the next webapp idea I get, I’m seriously considering doing that. Yeah, I know. I’m slow.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>All your APIs are belong to us</title>
   <link href="http://blog.knuthaugen.no//2009/08/all-your-apis-are-belong-to-us"/>
   <updated>2009-08-18T10:19:29+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/08/all-your-apis-are-belong-to-us</id>
   <content type="html">&lt;p&gt;I saw the link to &lt;a href=&quot;http://gotapi.com/&quot;&gt;gotapi.com&lt;/a&gt; link referenced from &lt;a href=&quot;http://stackoverflow.com/questions/63918/what-is-the-best-online-javascript-css-html-xhtml-dom-reference&quot;&gt;stackoverflow question 63918&lt;/a&gt; and if you’re like me and program in multiple languages and multiple frameworks it is the closest thing I’ve found to a one stop website for API references. It acts as a wrapper around API docs and specs from all over the web and covers things like Java API, JQuery (albeit an older version), Python, PHP, GWT, Ruby (and Rails), HTML, CSS XSL/XPATH/XSD, Perl and much more. And to top it off, you can cherrypick which ones to search in at &lt;a href=&quot;http://start.gotapi.com/&quot;&gt;start.gotapi.com&lt;/a&gt;. This replaces &lt;em&gt;a lot&lt;/em&gt; of bookmarks for me.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Luke - the Lucene index viewer</title>
   <link href="http://blog.knuthaugen.no//2009/08/luke-the-lucene-index-viewer"/>
   <updated>2009-08-17T10:20:25+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/08/luke---the-lucene-index-viewer</id>
   <content type="html">&lt;p&gt;I’m working on a web application at the moment which uses Lucene as a search engine and the &amp;lt;a href=”http://framework.zend.com/manual/en/zend.search.lucene.html”&amp;gt;Zend Lucene PHP port&amp;lt;/a&amp;gt;. And when debugging the indexing of documents I came across &amp;lt;a href=”http://www.getopt.org/luke/”&amp;gt;Luke&amp;lt;/a&amp;gt; a browser for the Lucene index file format. It’s a nifty tool and really the swiss army chainsaw you need when you can’t really search for what you thought you added or thought you added something that isn’t really searchable at all. It is written in Java and uses the java Lucene libraries but reads the Zend Lucene index file without problems (They’re compatible after all). It lets you view fields in the index and all sorts of information about them and browse all documents stored in the index and of course search it to actually see what you get. A very cool tool when it comes to debugging Lucene.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>On Ruby's elegance and Perl's lack of it</title>
   <link href="http://blog.knuthaugen.no//2009/07/on-rubys-elegance-and-perls-lack-of-it"/>
   <updated>2009-07-29T10:15:47+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/07/on-rubys-elegance-and-perls-lack-of-it</id>
   <content type="html">&lt;p&gt;I’ve been programming &lt;a title=&quot;Wikipedia on Perl&quot; href=&quot;http://en.wikipedia.org/wiki/Perl&quot;&gt;Perl&lt;/a&gt; for a number of years and I like the language a lot. It’s my weapon of choice when tackling some random programming task or just spiking something quickly. One thing that has bothered me over the years and surprised me to an extent is Perl’s rather lousy reputation. It’s criticized for being just line-noise, hard to read and generally frowned upon by people outside the Perl community. But Perl is a very flexible and powerful language. True you can write incomprehensible Perl, just as you can write gibberish in most any other language, but you can certainly write good, clean OO code. You just have to know what you are doing (or use &lt;a title=&quot;Moose CPAN page&quot; href=&quot;http://search.cpan.org/~drolsky/Moose-0.88/lib/Moose.pm&quot;&gt;Moose&lt;/a&gt; which is quite superb at covering up the transgressions of the Perl OO system).&lt;/p&gt;

&lt;p&gt;I recently picked up a copy of &lt;a title=&quot;Amazon: the ruby programming Language&quot; href=&quot;http://www.amazon.co.uk/Ruby-Programming-Language-David-Flanagan/dp/0596516177&quot;&gt;The Ruby Programming Language&lt;/a&gt; and the more I read the more it dawned on me just how much Ruby has gotten from Perl. This is no secret as the wikipedia page on Ruby spells out quite clearly. Yet Ruby is considered clean, elegant and straight-forward and has a much better reputation than Perl. Why is this? I decided to dig a little deeper. One point is that in Perl your effort, at least when new to Perl, must be on keeping the code from going all over the place and in Ruby you have to really try if you’re gonna write sloppy style. Wherein lies the actual differences?&lt;!--more--&gt;&lt;/p&gt;
&lt;h2&gt;The regex syntax&lt;/h2&gt;
&lt;p&gt;The regular expression syntax is quite similar in Ruby and Perl in that it’s expressed directly with &lt;code&gt;//&lt;/code&gt; and not requiring you to instantiate an Regex object first like in C# or Java.
Perl:&lt;/p&gt;
&lt;pre class=&quot;brush: perl&quot;&gt;
print &quot;match&quot; if  $string =~ /\w+?/;
&lt;/pre&gt;
&lt;p&gt;Ruby:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby&quot;&gt;
puts &quot;match&quot; if string =~ /\w+?/
&lt;/pre&gt;

&lt;p&gt;But here’s where Ruby fanatical devotion to the Pope, er, objects comes into play. In Perl you can choose to compile the regex once to avoid multiple regex engine compilations in a loop and store it in a variable. But all access to groups is done via $1,  $2 etc. . In Ruby this creates a new object of the &lt;a href=&quot;http://ruby-doc.org/core/classes/Regexp.html&quot;&gt;Regexp&lt;/a&gt; class hich you can either assign to a variable to use it multiple times or pass around or call methods on.
Ruby:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby&quot;&gt;
regex = /\w+?/
puts &quot;match&quot; if regex.match(&quot;foo&quot;)

# or:

/\w+?/.match &quot;foo&quot;
&lt;/pre&gt;

&lt;p&gt;and the match function returns MatchData object(s) which can be used for further digging. An you’ve got all sorts of cool functions like &lt;code&gt;union&lt;/code&gt; which returns a Regexp object which will match all the patterns given as arguments. Doing that in Perl can be very messy. All in all Ruby regexp handling can look like Perl’s but goes much further and gives you a lot more flexibility to do what you want in a clean and readable manner.&lt;/p&gt;
&lt;h2&gt;Variables and nested data structures&lt;/h2&gt;
&lt;p&gt;First there are to important distinctions: perl use special symbols on variables to indicate variable type, $ for scalars, @ for arrays and % for hashes. Ruby uses (fewer) symbols for indicating variable scope, namely $ for global variables, @ for instance variables, and @@ for class variables. For local variables you do without a sigil. But the more interesting point here is the syntax for defining nested hash tables and arrays in combination with these.&lt;/p&gt;

&lt;p&gt;defining an hash of arrays in Perl:&lt;/p&gt;
&lt;pre class=&quot;brush: perl&quot;&gt;
my %hash = ( 'key' =&amp;gt; [ '5', '3', '1' ] );

#or as a reference directly

my $hash =  { 'key' =&amp;gt; [ '5', '3', '1' ] };
&lt;/pre&gt;

&lt;p&gt;and in Ruby&lt;/p&gt;

&lt;pre class=&quot;brush: ruby&quot;&gt;
hash =  { 'key' =&amp;gt; [ '5', '3', '1'] }

#or with symbols as key

hash = { :key =&amp;gt; [ '5', '3', '1'] }
&lt;/pre&gt;

&lt;p&gt;Quite similar. But when we want to access the inner hash and use that one directly, for instance in a sort, look what happens when we have to de-reference the array ref inside the hash:
Perl:&lt;/p&gt;
&lt;pre class=&quot;brush: perl&quot;&gt;
my %hash = ( 'key' =&amp;gt; [ '5', '3', '1' ] );
@sorted = sort @{ $hash{'key'} };

#or with custom block for sorting reverse

@sorted = sort { $b &amp;lt;=&amp;gt; $a } @{ $hash{'key'} };
&lt;/pre&gt;

&lt;p&gt;Ruby:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby&quot;&gt;
hash = { :key =&amp;gt; [ '5', '3', '1'] }
sorted = hash[:key].sort

#or with custom block for sorting revers
sorted = hash[:key].sort { |a, b| b &amp;lt;=&amp;gt; a }
&lt;/pre&gt;

&lt;p&gt;Much less noise in the ruby version and I can certainly see that the Perl version, which wouldn’t be regarded as line noise at all to seasoned Perl programmer, could look intimidating to a java programmer. But the ruby way is more elegant. And it’s all about perception.&lt;/p&gt;

&lt;h2&gt;Block syntax&lt;/h2&gt;
&lt;p&gt;While we’re at it, let’s look at block syntax. This is not so much a noise issue as a power issue which also contributes to cleanliness in the code. Ruby’s block syntax with parameters as we saw in the previous example can be used for all sorts of cool things. A block is also an object in Ruby, which it isn’t in Perl.&lt;/p&gt;

&lt;pre class=&quot;brush: ruby&quot;&gt;
hash = { :b =&amp;gt; &quot;foo&quot;, :a =&amp;gt; &quot;bar&quot; }

hash.each { | key, value | puts &quot;#{key}: #{value}&quot; }

# or a multiline do..end block

hash.each do | key, value |
  puts &quot;#{key}: #{value}&quot;
end
&lt;/pre&gt;

&lt;p&gt;Let’s take an example comparing &lt;code&gt;map()&lt;/code&gt; from Perl and &lt;code&gt;Array.map&lt;/code&gt; in Ruby (including quoting array values to clean things up a bit in both Perl and Ruby).&lt;/p&gt;

&lt;pre class=&quot;brush: perl&quot;&gt;
@array = qw(a b c d e f);

#transform array to ASCII value for the elements
@chars = map { ord } @array;
&lt;/pre&gt;

&lt;pre class=&quot;brush: ruby&quot;&gt;
array = %w{a b c d e f}
chars = array.map { |x| x[0] }
&lt;/pre&gt;

&lt;p&gt;In this example the Perl version looks cleaner to me, but it will fail spectacularly when you need more than one parameter into the block. Ruby handles any number of block arguments, while Perl only has the one value from the data structure you’re mapping. This makes the &lt;code&gt;Hash.each_pair&lt;/code&gt; (with a block) very useful in Ruby while you cant really do the same without either using a &lt;code&gt;foreach&lt;/code&gt; on the keys or values or using map() on the keys and accessing values in the block. If you need both the key and the value inside the block that is. More code and less elegance.&lt;/p&gt;
&lt;h2&gt;Closing points&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Ruby's do..end syntax generally cleans up the language a bit, since this is in use for modules, classes, methods and blocks. Perl has only {} and this combined with other brackets and special characters can induce noise.&lt;/li&gt;
	&lt;li&gt;Combination of block or loop syntax with pattern matching can give a noisy appearance.
&lt;pre class=&quot;brush: perl&quot;&gt;
@array = ( [1, 2, 3], [4, 5, 6], [7, 8, 9]);
map { push @joined, sort { $b &amp;lt;=&amp;gt; $a } @{ $_ } }  @array;
&lt;/pre&gt;
and in ruby:
&lt;pre class=&quot;brush: ruby&quot;&gt;
array = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]
joined = Array.new
array.each { |x| joined.push(x.sort { |y,z| z &amp;lt;=&amp;gt; y }) }
&lt;/pre&gt;
Still not all that bad to a Perl hacker, but still.&lt;/li&gt;
	&lt;li&gt;Ruby OO strictness saves you a lot of typing if you're comparing to old school Perl OO. Moose takes much of that away nowadays.&lt;/li&gt;
	&lt;li&gt;Ruby's policy of optional parenthesis both on most method calls and if-statements removes some line-noise factor but can be less readable to the untrained eye. But you get used to it pretty quickly.&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Keyboard layout and remapping for fun and profit</title>
   <link href="http://blog.knuthaugen.no//2009/07/keyboard-layout-and-remapping-for-fun-and-profit"/>
   <updated>2009-07-22T10:18:27+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/07/keyboard-layout-and-remapping-for-fun-and-profit</id>
   <content type="html">&lt;p&gt;After having lived with some annoying CAPS-LOCK mishaps and a general feeling of pain in the lower forearm, I decided to remap my keyboard layout for a smoother programming experience. Since I’m Norwegian and use a Norwegian keyboard layout, I have to resort to AltGr+0 to produce a }. Brackets are equally far away. Parenthesis are shift+8 and shift+9 on the number row and all this means moving the hand away from the home row a lot. I wanted to be able to program and maintain my hands in a more or less touch typing position.The goal was improved speed at programming and less discomfort. The fact that I daily program on both windows and linux ( both native and via the cygwin X server) made things a bit more complex than anticipated, as we will see. &lt;!--more--&gt;This article tackles re-mapping the caps-lock key in windows and creating a new keyboard layout in both windows and linux (Xorg that is). Info about re-mapping the caps-lock key under linux is just a &lt;a href=&quot;//http://www.google.com/search?q=xmodmap+caps+alt_gr&quot;&gt;google search away&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following setup was used in this little adventure:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Windows XP running &lt;a href=&quot;http://www.cygwin.com/&quot;&gt;Cygwin&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;Ubuntu 9.04 (as X client only)&lt;/li&gt;
	&lt;li&gt;Xorg server 1.5.7 with Xkb packages running in cygwin.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Caps lock remapping&lt;/h2&gt;
&lt;p&gt;The caps lock key is irritating and I’ve never used as far as I can tell. But I haven’t gotten around to doing anything about it. Now’s the time. First I thought about remapping it to control and binding a lot the characters I want more accessible to ctrl+ bindings. This messes with application shortcuts a lot so I went for altGr instead. I used a small program named &lt;a href=&quot;http://webpages.charter.net/krumsick/&quot;&gt;KeyTweak&lt;/a&gt; to perform the binding. The result of running that program is that i new value is written to the registry. It looks like this:&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]

&quot;Scancode Map&quot;=hex:00,00,00,00,00,00,00,00,02,00,00,00,38,e0,3a,00,00,00,00,00
&lt;/pre&gt;

&lt;p&gt;Either apply the file or do it from within KeyTweak and the caps lock key is now an AltGr key. A reboot is probably in order to put it into effect. It is windows, after all.&lt;/p&gt;
&lt;h2&gt;Keyboard layout for windows&lt;/h2&gt;
&lt;p&gt;I used a Microsoft tool called &lt;a href=&quot;http://www.microsoft.com/downloads/details.aspx?FamilyID=8be579aa-780d-4253-9e0a-e17e51db2223&amp;amp;DisplayLang=en&quot;&gt;Microsoft Keyboard Layout Creator&lt;/a&gt; which is a nice tool for creating a new layout from scratch or basing it on an existing layout. It has some limitations however in that you can’t remap things like caps lock and the windows key. But on the other hand it generates nice install packages for your keyboard layout. I decided on the following keyboard shortcuts (remember caps lock is now AltGr so it is easily available for the left pinky finger):&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
	AltGr+a = !
	AltGr+s = #
	AltGr+d = @
	AltGr+f = $
	AltGr+g = (
	AltGr+h = )
	AltGr+j = /
	AltGr+k = \
	AltGr+l = ~
	AltGr+&amp;oslash; = =
	AltGr+&amp;aelig; = +
	AltGr+u = {
	AltGr+i = [
	AltGr+o = ]
	AltGr+p = }
&lt;/pre&gt;

&lt;h2&gt;Keyboard layout for linux/ Xorg&lt;/h2&gt;
&lt;p&gt;When applying the custom layout for windows this worked well for native windows apps, but when logging into the linux servers and X-forwarding apps to the cygwin X server, it naturally didn’t work. A similar custom keyboard layout for the X server was needed.&lt;/p&gt;

&lt;p&gt;I found a great &lt;a href=&quot;http://hektor.umcs.lublin.pl/~mikosmul/computing/articles/custom-keyboard-layouts-xkb.html&quot;&gt;detailed guide&lt;/a&gt; on creating keyboad layouts for X11/Xorg with XKB. There are some steps involved:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Copy the layout you want to base you new layout on to a new file in /usr/share/X11/xkb/symbols.&lt;/li&gt;
	&lt;li&gt;Tweak it to your liking.&lt;/li&gt;
	&lt;li&gt;Add it to the list of supported keyboard layouts in /usr/share/X11/xkb/rules/xfree86.lst&lt;/li&gt;
	&lt;li&gt;Restart your X server and use &lt;code&gt;setxkbmap&lt;/code&gt; to set it, or add it in xorg.conf&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The interesting parts of the symbols file looks like this:&lt;/p&gt;
&lt;pre class=&quot;brush: bash&quot;&gt;
key { [	u,	U,	braceleft,		uparrow ] };
key { [	i,	I, 	bracketleft,	idotless ] };
key { [	o,	O,	bracketright,	Ooblique ] };
&lt;/pre&gt;

&lt;p&gt;This snippet maps e.g. AltGr+u to braceleft which displays a nice left curly brace.
The format is broken down like this:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The key name AD07 is the 7th (07) alphanumeric (A) key from the left of the fourth row (D) from the bottom. Bottom row is A, second to bottom B and so on.&lt;/li&gt;
	&lt;li&gt;First column is character displayed when the key is pressed normally&lt;/li&gt;
	&lt;li&gt;Second column is character displayed when the key is pressed with shift down&lt;/li&gt;
	&lt;li&gt;Third column is character displayed when AltGr is down&lt;/li&gt;
	&lt;li&gt;Fourth column is character displayed when both AltGr and shift is down&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Key names can be found in &lt;code&gt;/usr/X11/include/X11/keysymdef.h&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The keyboard layout is exactly the same as for windows and I just based it on the standard Norwegian layout and called it progn (&lt;a href=&quot;http://www.franz.com/support/documentation/current/ansicl/dictentr/progn.htm&quot;&gt;pun intented&lt;/a&gt;). The entire progn keyboard symbols file is available &lt;a href=&quot;http://knuthaugen.no/blog/media/progn&quot;&gt;for download&lt;/a&gt;. So now I have remapped the keyboard to my liking in both Linux and windows. I’ll probably tweak the keymappings a bit after some use, but generally I’m very happy with them.
&lt;br /&gt;
&lt;strong&gt;References:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.cygwin.com/&quot;&gt;http://www.cygwin.com/&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://hektor.umcs.lublin.pl/~mikosmul/computing/articles/custom-keyboard-layouts-xkb.html&quot;&gt;http://hektor.umcs.lublin.pl/~mikosmul/computing/articles/custom-keyboard-layouts-xkb.html&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://webpages.charter.net/krumsick/&quot;&gt;http://webpages.charter.net/krumsick/&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://vlaurie.com/computers2/Articles/remap-keyboard.htm&quot;&gt;http://vlaurie.com/computers2/Articles/remap-keyboard.htm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Videos from Lean &amp; Kanban 2009</title>
   <link href="http://blog.knuthaugen.no//2009/07/videos-from-lean-kanban-2009"/>
   <updated>2009-07-02T10:17:34+02:00</updated>
   <id>http://blog.knuthaugen.no//2009/07/videos-from-lean-kanban-2009</id>
   <content type="html">&lt;p&gt;A nearly complete set of videos of the talks at the Lean &amp;amp;amp; Kanban 2009 conference in Miami is available at &lt;a href=&quot;http://www.sep.com/lk2009&quot;&gt;http://www.sep.com/lk2009&lt;/a&gt; with talks from among others Corey Ladas on &lt;a href=&quot;http://leansoftwareengineering.com/ksse/scrum-ban/&quot;&gt;scrumban&lt;/a&gt;, Alan Shalloway on going beyond Toyota, David Anderson on Kanban, Karl Scotland on Kanban flow and cadence and Amit Rethore on why agile isn’t enough. All familiar names from the kanbandev Yahoo group which I started to follow recently.&lt;br /&gt;&lt;br /&gt;Now I just hope I find enough time to watch some of them.&lt;/p&gt;
</content>
 </entry>
 
 
</feed>
