<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Rocket-Powered Jet Pants</title>
    <link>https://www.rocketpoweredjetpants.com/</link>
    <description>Recent content on Rocket-Powered Jet Pants</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 12 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.rocketpoweredjetpants.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>We&#39;re Going to Make Out Like Bandits</title>
      <link>https://www.rocketpoweredjetpants.com/2026/04/were-going-to-make-out-like-bandits/</link>
      <pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2026/04/were-going-to-make-out-like-bandits/</guid>
      <description>&lt;p&gt;Here’s the plan. I reckon it’ll take about five years in all, and I
think we’re about 1.5 to 2 years in already.&lt;/p&gt;
&lt;p&gt;Our starting point is that the AI models are now good enough at
coding. Not necessarily “perfect”, just “good enough”. Often, they&amp;rsquo;re
as good (better!)  than a junior developer. I don’t know about you,
but I’m finding the new models pretty impressive. So are other people.&lt;/p&gt;
&lt;p&gt;Next, let’s “eat the seed corn”. Junior developers can now be
considered (by some) to be superfluous to our needs; for the same
cost, we could get soooo many tokens, and get so much more done! &lt;a href=&#34;https://www.bbc.co.uk/news/articles/cm21dvg8l1go&#34;&gt;Job
openings for junior
devs&lt;/a&gt; will dry
up. &lt;a href=&#34;https://digitaleconomy.stanford.edu/publication/canaries-in-the-coal-mine-six-facts-about-the-recent-employment-effects-of-artificial-intelligence/&#34;&gt;This is already
happening&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the interesting things about LLMs is that they love to generate
new code. Given their vast corpus of training data, they also know how
to write those supporting functions and methods we commonly get from
third party libraries. The result? We’ll start writing more and more
code. The size of our repos will balloon. &lt;a href=&#34;https://arxiv.org/pdf/2601.21276&#34;&gt;This is already
happening&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, we also know that the tendency of the AIs isn’t to go and clean
up code, reduce duplication, or focus on maintainability. Context
windows (even the large ones!) can’t hold entire modern repos. They
miss things. Most AI written code is additive, and is frequently
duplicative. &lt;a href=&#34;https://www.gitclear.com/ai_assistant_code_quality_2025_research&#34;&gt;This is already
happening&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Machines have a higher “tolerance” for complexity than people, which
means we can now bear higher complexity budgets and tech debt. That
is, modern AIs tend to be very good at reading code and following the
flow of control, &lt;a href=&#34;https://red.anthropic.com/2026/mythos-preview/&#34;&gt;often faster and better than
people&lt;/a&gt;. However,
“debugging is twice as hard as writing a program in the first
place. So if you’re as clever as you can be when you write it, how
will you ever debug it?”  still holds true. At some point the tech
debt and complexity will be so high even the AIs won’t be able to deal
with it. We’ve already frequently blown past human levels of code
complexity. &lt;a href=&#34;http://rocketdevs.com/blog/AI-Technical-Debt-Crisis&#34;&gt;This is already
happening&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And code is never bug free. &lt;a href=&#34;https://www.theregister.com/2025/12/17/ai_code_bugs/&#34;&gt;Defect rates in AI generated
code&lt;/a&gt; are
typically above that of human-written code, but even if it drops below
that, because so much code is being written, the overall number of
defects will climb. Because code is seldom properly factored, fixing a
bug in one place won’t fix it everywhere. We’ll play whack-a-mole with
bugs. &lt;a href=&#34;https://www.gitclear.com/ai_assistant_code_quality_2025_research&#34;&gt;This is already
happening&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, we’re going to end up with (we already have!) complex, poorly
structured codebases, rife with bugs and duplicated code. Who has the
judgment to decide what to delete, which abstraction is wrong, or
whether the whole approach needs rethinking? Who do we call in to fix
this kind of mess?&lt;/p&gt;
&lt;p&gt;Senior developers.&lt;/p&gt;
&lt;p&gt;However, there’s attrition in the industry. Senior developers leave,
not only because of the regular attrition that occurs over time, but
also driven by a &lt;a href=&#34;https://leaddev.com/culture/engineering-burnout-rising-2025-layoffs-reshape-tech-industry&#34;&gt;22% spike in critical
burnout&lt;/a&gt;
as they are forced to manage the massive influx of AI-generated
complexity. Because we’ve stopped hiring juniors, there are few new
seniors coming up to replace them. The ones who do make it through
haven’t experienced life before AI; they’ll be good, but they won’t
necessarily be great at writing maintainable code. In a market where
&lt;a href=&#34;https://beon.tech/blog/software-development-talent-shortage/&#34;&gt;senior, production-ready
engineers&lt;/a&gt;
are already the primary bottleneck, I think we’ll need seasoned
developers with good taste more than ever.&lt;/p&gt;
&lt;p&gt;In any market where there’s increased demand and reduced supply,
prices go up. This happened for the COBOL programmers of yore during
and shortly after Y2K. It’ll happen for senior developers soon, and
just like our COBOL-wielding brethren before us, there will a shining
window of opportunity. We just need to hunker down and survive the
storm of AI-driven layoffs.&lt;/p&gt;
&lt;p&gt;Then, my friends, we’re going to make out like bandits.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Dorodango Technique: Agents in Agents Driving Scripts</title>
      <link>https://www.rocketpoweredjetpants.com/2026/01/the-dorodango-technique-agents-in-agents-driving-scripts/</link>
      <pubDate>Thu, 29 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2026/01/the-dorodango-technique-agents-in-agents-driving-scripts/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;“Agents in agents driving scripts” is a pattern for handling large-scale, mechanical changes using AI. Each layer of the pattern is developed iteratively, using failures from previous runs to refine either the prompts or the scripts being used.&lt;/p&gt;
&lt;h2 id=&#34;the-long-version&#34;&gt;The long version&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m giving a worked example in this post in order to demonstrate how to apply the idea. This post isn&amp;rsquo;t about either bazel or gazelle. With that out the way&amp;hellip;.&lt;/p&gt;
&lt;p&gt;I’ve been exploring how I might generate build files in a large Bazel-based repo which currently uses hand-crafted build files. The theory of the migration is simple. We know the code compiles now, and that information is encoded in the build graph. We can use &lt;code&gt;bazel query&lt;/code&gt; to inspect this graph, and the &lt;code&gt;graph&lt;/code&gt; output format makes it possible to topologically sort these by their dependencies. Once that’s done, we can start the migration, beginning at the directories with no dependencies and ending with those which need the entire build graph migrated.&lt;/p&gt;
&lt;p&gt;We can tell whether a build file is being managed by gazelle by checking that neither the &lt;code&gt;exclude&lt;/code&gt; nor &lt;code&gt;ignore&lt;/code&gt; annotation is present.&lt;/p&gt;
&lt;p&gt;With this information in mind, the process starts by setting up gazelle in the repo, and updating all the build files to indicate that gazelle shouldn’t process them by adding the &lt;code&gt;exclude&lt;/code&gt; directive. Once that’s done the fun can begin.&lt;/p&gt;
&lt;p&gt;Ultimately, I know the process of migrating a build file to gazelle is pretty mechanical. I also know that running a script is often faster, cheaper, and more deterministic than using an LLM for the same task.&lt;/p&gt;
&lt;p&gt;But we don’t have the script yet. So we use an LLM to write one.&lt;/p&gt;
&lt;p&gt;To begin with, I start an interactive LLM and work with it to migrate a single directory. Once I’m happy with that, I ask the LLM to create a (python!) script to perform the same actions. The key thing here is to insist that if there were any errors or problems, the script must fail and report the problem.&lt;/p&gt;
&lt;p&gt;The next step is to write a prompt that uses this script and write that to a file. The prompt checks that we’re ready to start (is the build still green, is running gazelle a no-op, etc) and then runs the script to migrate the directory, and then is told how to validate that the change is safe (normally very similar to the start conditions, with some extra checks). The end goal is to “one shot” an update, so the prompt is told to stop if there are any issues at all.&lt;/p&gt;
&lt;p&gt;This is where the iterative improvement happens. Each time an issue stops the process, we use that failure to immediately update the script, add additional scripts, or refine the original prompt to avoid future problems. This continuous feedback loop ensures that the next iteration will be smoother&lt;/p&gt;
&lt;p&gt;Once we achieve this single-directory &amp;lsquo;one-shot&amp;rsquo; success, we move up a level, and start on an orchestration prompt. Again, we start this manually. My approach is to tell the orchestrator to start a number of subagents, each of which should use the polished prompt and script we’ve developed above. Again, if there are any problems, the orchestrator should stop everything and report the issues, grouping them as required. Each time we resolve a problem, we either ask for the scripts or the prompts to be updated. It’s an iterative process.&lt;/p&gt;
&lt;p&gt;Then we get to the fun bit.&lt;/p&gt;
&lt;p&gt;We start the orchestration agent using our orchestration prompt, that fires up the subagents using the prompt for the migration, and that uses the scripts we’ve been refining. Agents in agents driving scripts.&lt;/p&gt;
&lt;h2 id=&#34;why-dorodango&#34;&gt;Why “Dorodango”?&lt;/h2&gt;
&lt;p&gt;Dorodango is the Japanese art of polishing mud, taking something that’s rough and unformed and, by iterating and smoothing, making a beautiful sphere out of the most unlikely starting points. In the same way, this pattern takes a rough and unformed idea, and by iterating and smoothing ends up with an efficient way of managing change.&lt;/p&gt;
&lt;h2 id=&#34;comparison-with-wiggum-loops&#34;&gt;Comparison with Wiggum Loops&lt;/h2&gt;
&lt;p&gt;While both the Dorodango Technique and &lt;a href=&#34;https://ghuntley.com/ralph/&#34;&gt;Wiggum Loops&lt;/a&gt; are methodologies for using LLMs in iterative, self-correcting development, they differ in their structure and approach to failure.  The Dorodango technique is about iterating on the design (the scripts and prompts) &lt;strong&gt;after&lt;/strong&gt; a failure in order to build a robust system. It uses the LLM to write the system, but &lt;em&gt;you&lt;/em&gt; are in the loop to refine it. Wiggum Loops are about iterating on the code/output until the LLM successfully completes a well-defined task autonomously. It is designed for maximum automation once the loop is started.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Principles of software development</title>
      <link>https://www.rocketpoweredjetpants.com/2025/05/principles-of-software-development/</link>
      <pubDate>Tue, 20 May 2025 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2025/05/principles-of-software-development/</guid>
      <description>&lt;p&gt;I’ve written code which is still in widespread use today that’s old enough to be considered an adult in the UK. I’m pretty proud of that. I’ve also written plenty of code that was absolutely terrible. Sometimes these bits of code are the same. I’ve learned from that.&lt;/p&gt;
&lt;p&gt;I’ve had the pleasure of working with some brilliant software engineers who opened my eyes to better ways of working. I’ve also worked with developers who were still learning the craft, or who didn’t care for the thing they were working on. That taught me things too.&lt;/p&gt;
&lt;p&gt;As I write this, I’m on a plane armed with only my phone, so perhaps that will help me keep things concise, but I think it might be useful if I outlined some of my principles for software development. So, without further ado, here they are, pulled from the aether at 30,000 feet!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The best code is the code you don’t write&lt;/li&gt;
&lt;li&gt;Fast feedback loops are vital&lt;/li&gt;
&lt;li&gt;Assume no one reads the docs&lt;/li&gt;
&lt;li&gt;Favour simplicity&lt;/li&gt;
&lt;li&gt;Do the simplest thing that can possibly work&lt;/li&gt;
&lt;li&gt;Fear of code is a sign of where to start&lt;/li&gt;
&lt;li&gt;Collocate things that are related&lt;/li&gt;
&lt;li&gt;Long lived code needs tests&lt;/li&gt;
&lt;li&gt;Source control gives you freedom&lt;/li&gt;
&lt;li&gt;Don’t let “perfect” be the enemy of “good”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-best-code-is-the-code-you-dont-write&#34;&gt;The best code is the code you don’t write&lt;/h2&gt;
&lt;p&gt;The advice is simple: avoid writing code if you can.&lt;/p&gt;
&lt;p&gt;Why? Because every line of code you write is one that needs to be maintained, tested, and otherwise cared for. If you can avoid needing to do that, you can focus your limited time on other, more exciting,  things.&lt;/p&gt;
&lt;p&gt;If there’s an existing tool that does what you want, use it. If there’s a library out there which supports the features you want, go with that. If a shared calendar would work, use that instead of creating some bespoke booking system.&lt;/p&gt;
&lt;p&gt;We all know that not everything will be a perfect fit for you. That’s fine. Sometimes you can adjust your processes or approach. Alternatively, you can try and modify or extend whatever it is that needs to be changed. And you know what? Sometimes, it might just be better to write something of your own, but that should be a last resort after considering the alternatives. As a rule of thumb: don’t write that code!&lt;/p&gt;
&lt;p&gt;Attempting to make changes is especially important when dealing with Open Source software. &lt;strong&gt;If it’s Open Source, contribute upstream instead of maintaining a fork&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It may be painful thanks to whatever combination of corporate and OSS politics and personalities you need to wrestle with, but contributing back is the Right Thing to do. Most importantly (for me!) it means that you’ve improved part of the shared commons, and that allows us all to move forward. However, if an appeal to altruism isn’t something that appeals to you, contributing your patches means you won’t need to maintain a patch or fork moving forwards. Contribute enough and you can build a body of work to advertise yourself with.&lt;/p&gt;
&lt;p&gt;Just remember, if someone else’s solution is better than yours, it’s fine to abandon your efforts for theirs. We want to solve problems, not massage our egos.&lt;/p&gt;
&lt;h2 id=&#34;fast-feedback-loops-are-vital&#34;&gt;Fast feedback loops are vital&lt;/h2&gt;
&lt;p&gt;This is the biggie for me.&lt;/p&gt;
&lt;p&gt;You can’t know if you’re heading in the right direction without feedback. The sooner you get it, the sooner you can course correct (or carry on doing more of the same). If feedback loops are long, by the time you discover something went wrong you&amp;rsquo;ve often lost the context needed to fix it quickly or cleanly.&lt;/p&gt;
&lt;p&gt;This is one reason why I like compiled languages. There’s no faster feedback than the IDE telling you “this thing won’t even compile”&lt;/p&gt;
&lt;p&gt;It’s also why I’ve spent the past 10 years or so working on build systems. I want the distance between finishing a thought and finding out whether it works to be as short as possible. Dan Bodart coined the term “&lt;a href=&#34;https://web.archive.org/web/20230410230336/http://dan.bodar.com/2012/02/28/crazy-fast-build-times-or-when-10-seconds-starts-to-make-you-nervous/&#34;&gt;10 second build&lt;/a&gt;”, and I think that’s something to strive for.&lt;/p&gt;
&lt;p&gt;Smaller changes being pushed to production more quickly also lowers the risk of each change. Faster feedback is safer feedback too.&lt;/p&gt;
&lt;p&gt;I’m willing to give up a lot for a tight feedback loop, but with modern tooling, distributed builds, and careful design choices, I usually don’t need to.&lt;/p&gt;
&lt;h2 id=&#34;assume-no-one-reads-the-docs&#34;&gt;Assume no one reads the docs&lt;/h2&gt;
&lt;p&gt;“I’d rather spend an afternoon debugging and struggling than 10 minutes reading the docs”&lt;/p&gt;
&lt;p&gt;Documentation is read at most twice: once when we start using something, and once when we’re really lost and can’t find the answer anywhere else. That’s certainly true for me. My experience tells me that’s true for many of you too.&lt;/p&gt;
&lt;p&gt;This obviously impacts how I design APIs that I expect others to use. This is also why I like uniformity and consistency around how things work.&lt;/p&gt;
&lt;p&gt;Put another way, you can rephrase this as “don’t make me think”. As someone solving a problem, I really don’t want to be taken out of the flow to deal with figuring out some weird little detail. It’s one of the reasons why I like tooling that works the same way for the same kinds of tasks across different languages.&lt;/p&gt;
&lt;p&gt;This should also colour how documentation is written. If you assume the reader hasn’t read the rest of the documentation or the source code, but comes with a half-formed query (and probably a &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2013/01/clue/&#34;&gt;certain amount of frustration&lt;/a&gt; if they’ve been working on a problem for a while), how would you want the documentation to look? How would it best support them?&lt;/p&gt;
&lt;p&gt;Maybe in the future this won’t matter so much. After all, the AIs &lt;em&gt;have&lt;/em&gt; read the docs, and they’re excellent at retrieving knowledge. Perhaps the consistency I crave will be the friendly prompt of my AI assistant, one level removed from the daily grind of the tooling and APIs I regularly use.&lt;/p&gt;
&lt;p&gt;But for now…&lt;/p&gt;
&lt;h2 id=&#34;favour-simplicity&#34;&gt;Favour simplicity&lt;/h2&gt;
&lt;p&gt;There’s a &lt;a href=&#34;https://www.goodreads.com/author/quotes/153350.Brian_W_Kernighan&#34;&gt;quote from Kernighan&lt;/a&gt; that states “Everyone knows that debugging is twice as hard as writing a program in the first place. So if you&amp;rsquo;re as clever as you can be when you write it, how will you ever debug it?” That alone makes a good case for simplicity.&lt;/p&gt;
&lt;p&gt;But for me, simplicity isn’t just about debugging. It’s more than that. As someone trained to spot patterns, I’m predisposed to create premature abstractions. Favouring simplicity reminds me to resist that urge and just solve the problem in front of me.&lt;/p&gt;
&lt;p&gt;This is good for any number of reasons, not least of which is that I tend to get done sooner; a faster feedback loop.&lt;/p&gt;
&lt;p&gt;I think this is also part of being a good neighbour. After all, I’m sure we’ve all wandered into codebases where simplicity was an afterthought. It’s seldom a joyful experience.&lt;/p&gt;
&lt;p&gt;Having said this, I should acknowledge the idea of “&lt;a href=&#34;https://en.wikipedia.org/wiki/No_Silver_Bullet&#34;&gt;essential complexity&lt;/a&gt;”. Sometimes things are complex by nature. I’m okay with that, just so long as we’ve boiled away as much of the “accidental complexity” as possible. After all, &lt;a href=&#34;https://grugbrain.dev&#34;&gt;complexity is the enemy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;do-the-simplest-thing-that-can-possibly-work&#34;&gt;Do the simplest thing that can possibly work&lt;/h2&gt;
&lt;p&gt;More simplicity? Yes! It’s so important, I mention it twice!&lt;/p&gt;
&lt;p&gt;Doing the simplest thing helps rein in my tendency to over-abstract early. Abstractions reveal themselves with time. Let me repeat that: you&amp;rsquo;d need a &lt;a href=&#34;https://en.wikipedia.org/wiki/Paul_Atreides&#34;&gt;Muad’Dib&lt;/a&gt;-level gift for prescience to accurately anticipate where the real seams will appear in a problem you’ve yet to solve.&lt;/p&gt;
&lt;p&gt;When writing code, I take this to mean writing something that’ll work now, but which I know can be changed later. After all, code is infinitely plastic, and if we allow it we can reshape it indefinitely.&lt;/p&gt;
&lt;p&gt;Now, I’ve seen this applied maliciously too as “do the stupidest thing that will possibly work”. Don’t do that. That’s the kind of thing foolish people with little care for others do.&lt;/p&gt;
&lt;p&gt;The core idea is to give yourself &lt;strong&gt;options&lt;/strong&gt;. Chris Matts expressed this well with the concept of &lt;a href=&#34;https://www.leadingagile.com/2008/07/understanding-real-options/&#34;&gt;Real Options&lt;/a&gt;: options have value, they expire, and you should never commit early unless you know why. You may have heard this expressed as “making decisions at the last responsible moment.”&lt;/p&gt;
&lt;h2 id=&#34;fear-of-code-is-a-sign-of-where-to-start&#34;&gt;Fear of code is a sign of where to start&lt;/h2&gt;
&lt;p&gt;You own your code – it shouldn’t own you.&lt;/p&gt;
&lt;p&gt;If there’s a place in your code base that you fear to tread, view that as an invitation to tame the horror and deal with the complexity, not as a reason to shy away and retreat.&lt;/p&gt;
&lt;p&gt;Fear spreads if you don’t address it. What starts as a small area that troubles us leads to bigger, more terrifying things.&lt;/p&gt;
&lt;p&gt;I’ve seen this manifest so many times as sclerotic and moribund code bases, where people are scared to change anything lest the whole thing collapse. That leads to more bureaucracy, more caution, and delays implementing new patterns and approaches. That, in turn, kills your feedback loop.&lt;/p&gt;
&lt;p&gt;Which, as you now know, is a thing I can’t abide.&lt;/p&gt;
&lt;p&gt;So! Use fear as a guide for where to go next. It may not be comfortable, but it’ll be a step towards simplicity and faster feedback loops, and that’s an unalloyed good.&lt;/p&gt;
&lt;h2 id=&#34;collocate-things-that-are-related&#34;&gt;Collocate things that are related&lt;/h2&gt;
&lt;p&gt;This principle is foundational to object-oriented programming: keep data and behavior close together. The thing is, this principle applies to more than just a style of programming.&lt;/p&gt;
&lt;p&gt;For example, if two codebases are tightly coupled, they should live in the same repository. Yes, that might mean fewer, larger repos. That’s fine because by collecting coupled things together, we front-load the moment where we integrate them. It may make an individual change seem more painful, as suddenly all the integration points are known and need to be fixed, but all we’ve done is move those fixes earlier in the development cycle, and made them visible. Earlier fixes mean tighter feedback loops.&lt;/p&gt;
&lt;p&gt;(See? I told you I liked those.)&lt;/p&gt;
&lt;h2 id=&#34;long-lived-code-needs-tests&#34;&gt;Long lived code needs tests&lt;/h2&gt;
&lt;p&gt;How do you know whether a change you’ve made is safe and correct? As &lt;a href=&#34;http://www.natpryce.com/index.html&#34;&gt;Nat Pryce&lt;/a&gt; once quipped “I can make changes really fast if I don’t have to prove they work”, so one way is to YOLO it and hope for the best.&lt;/p&gt;
&lt;p&gt;But relying on production to tell you whether something’s broken is the longest possible feedback loop. And the bigger your system gets, the more fragile that becomes.&lt;/p&gt;
&lt;p&gt;So: write tests.&lt;/p&gt;
&lt;p&gt;What kind of tests? Context matters, but my rule of thumb is: &lt;strong&gt;the smaller, the better.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I like &lt;a href=&#34;https://testing.googleblog.com/2010/12/test-sizes.html&#34;&gt;Google’s test size model&lt;/a&gt;, which I once wrote about on their testing blog. However you choose to name your tests, aim for speed and precision. Small tests run fast and isolate failures well, even if their individual coverage is limited. In aggregate, they’re powerful.&lt;/p&gt;
&lt;p&gt;Of course, you also need some larger tests too, but it’s likely your CI will run for too long and will be prone to flakiness if they form the bulk of your testing. It&amp;rsquo;s why I’m still a fan of the &lt;a href=&#34;https://martinfowler.com/bliki/TestPyramid.html&#34;&gt;testing pyramid&lt;/a&gt;, even though it’s a model that some people find dated.&lt;/p&gt;
&lt;p&gt;But doesn’t writing tests mean you can’t write as much production code? While that’s true in the short term, I’m firmly convinced that a lack of tests means more bugs, which means more bug fixing, which means less time writing production code in the long term.&lt;/p&gt;
&lt;p&gt;This suggests that all code should have tests, right? Not really.&lt;/p&gt;
&lt;p&gt;If you don’t expect the code to live long, or for it to be an exploration of a problem you’re using to expand your knowledge, it’s quite alright not to have any tests at all. Dan Terhorst-North talks about the pattern of “&lt;a href=&#34;https://lizkeogh.com/2012/06/24/beyond-test-driven-development/&#34;&gt;spike and stabilise&lt;/a&gt;”, which is a useful way of figuring out which tests are needed, and as Liz Keogh points out, it’s a powerful way of getting fast feedback from stakeholders.&lt;/p&gt;
&lt;p&gt;One last thing: I find writing tests after the fact boring and thankless. That’s why I’m a fan of &lt;a href=&#34;http://www.extremeprogramming.org/rules/testfirst.html&#34;&gt;TDD&lt;/a&gt;. Even if I throw some tests away later, starting with them helps me focus and clarifies what I’m trying to achieve.&lt;/p&gt;
&lt;h2 id=&#34;source-control-gives-you-freedom&#34;&gt;Source control gives you freedom&lt;/h2&gt;
&lt;p&gt;I’ve been on a number of projects where dead code is kept around, sometimes in an “archive” directory, or (more frequently) still in place, untouched and unloved. This has always puzzled me. You use source control (you do use source control, right?) and that means it’s perfectly safe to delete code: it’s still there in our source control system if we need it.&lt;/p&gt;
&lt;p&gt;Similarly, if you want to try out something new or a risky change, you can do that with complete confidence that you can go back to a known good state without needing any fancy shenanigans. A single “git checkout” and you’re back where you started.&lt;/p&gt;
&lt;p&gt;By eliminating dead code entirely, you reduce the maintenance cost of our code (coincidentally allowing faster feedback loops, as there’s less code to compile and test) By being able to experiment freely, you can reduce our fear of the codebase, which can help you tame it.&lt;/p&gt;
&lt;p&gt;Source control is your safety net. Use it. Trust it. It won’t let you down.&lt;/p&gt;
&lt;h2 id=&#34;dont-let-perfect-be-the-enemy-of-good&#34;&gt;Don’t let “perfect” be the enemy of “good”&lt;/h2&gt;
&lt;p&gt;The temptation when working on a system is to want to be able to make it work for all cases, all the time. But often, solving a few cases well – without making others worse – is already a huge improvement.&lt;/p&gt;
&lt;p&gt;Until code is in production, it has no value. So ship something. Learn from how people use it. Iterate.&lt;/p&gt;
&lt;p&gt;A couple of examples of this spring to mind.&lt;/p&gt;
&lt;p&gt;Meta was notorious for its mantra of “move fast and break things”. Many people focused on the second half of that slogan, but that was merely emphasising how important it was to be able to move fast. By putting something out there and seeing how people responded to it, Meta were able to nimbly adjust a project’s direction, even if that sometimes meant that not everything was working as well as it should.&lt;/p&gt;
&lt;p&gt;Or take the example of how we &lt;a href=&#34;https://github.com/bazel-contrib/rules_jvm/tree/main/java/gazelle&#34;&gt;generate Bazel build files for Java&lt;/a&gt; using a tool. To do this perfectly would have been to have special-case handling for cases that seldom occur, and to generalise for all manner of source code layouts. However, it turns out that just having &lt;em&gt;anything&lt;/em&gt; in place offers value, and makes people’s lives better.&lt;/p&gt;
&lt;p&gt;Don’t worry about solving every problem. Start by solving one, then take it from there. Doing so tightens your feedback loops, and that can only be a good thing.&lt;/p&gt;
&lt;h2 id=&#34;wrapping-up&#34;&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;If I gave this more thought, I’m sure the list would be longer, and if I did so I am confident you’d lose patience reading this!&lt;/p&gt;
&lt;p&gt;I’m also sure that reasonable people would make different choices, or have entirely different principles. That’s good. The gentle tension between approaches often leads to better outcomes. All we need to do is assume the best of each other.&lt;/p&gt;
&lt;p&gt;In a way, a lot of these principles are self-reinforcing. Simplicity and not letting “perfect” be the enemy of “good” can be seen as two sides of the same coin. Having tests makes it easier to be fearless. Colocating things really does lead to faster feedback loops. That begs the question: how short could this list be? If I reduced this down to the very smallest number, I think I’d end up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be fearless&lt;/li&gt;
&lt;li&gt;Fast feedback loops are essential&lt;/li&gt;
&lt;li&gt;Code is worthless until it’s being used&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Software development is more than just a set of principles. I am utterly convinced that writing software is a team sport and the most important thing on any software project is the people. Finding ways to work together effectively is far better than sticking dogmatically to The One True Way. Maybe that’s a blog post for another day….&lt;/p&gt;
&lt;p&gt;But for now, it’s pretty hard to type this much on a phone keyboard while jetting through the air, so I’m going to stop here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>rules_jvm_external&#39;s New Maven-based Resolver</title>
      <link>https://www.rocketpoweredjetpants.com/2024/04/rules_jvm_externals-new-maven-based-resolver/</link>
      <pubDate>Fri, 26 Apr 2024 20:16:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2024/04/rules_jvm_externals-new-maven-based-resolver/</guid>
      <description>&lt;p&gt;The code you&amp;rsquo;ve written means nothing until it&amp;rsquo;s in the hands of
users.&lt;/p&gt;
&lt;p&gt;Normally, the way I like to write features is to put together the
smallest thing I can think of, release that, and then incrementally
add features and fix bugs. This lowers risk, and makes it far easier
to move quickly, but sometimes that&amp;rsquo;s just not possible.&lt;/p&gt;
&lt;p&gt;With the &lt;a href=&#34;https://github.com/bazelbuild/rules_jvm_external/releases/tag/6.1&#34;&gt;new release of &lt;code&gt;rules_jvm_external&lt;/code&gt; 6.1&lt;/a&gt;, I finally
managed to ship a new &lt;a href=&#34;https://github.com/bazelbuild/rules_jvm_external/blob/6.1/private/tools/java/com/github/bazelbuild/rules_jvm_external/resolver/maven/MavenResolver.java&#34;&gt;Maven-based dependency resolver&lt;/a&gt;. I
started work on this at the tail end of 2022, with the first proper
commit in late January 2023, so it&amp;rsquo;s been in development a loooong
time. I&amp;rsquo;ve not actually spent &lt;em&gt;that&lt;/em&gt; much time hacking on it. It&amp;rsquo;s
taken a long time because it&amp;rsquo;s something that I&amp;rsquo;ve hacked in when I
have a little time to spare.&lt;/p&gt;
&lt;p&gt;The main motivation behind this was to add support for using &lt;a href=&#34;https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#bill-of-materials-bom-poms&#34;&gt;Maven
BOMs&lt;/a&gt; in dependency resolutions for Bazel projects, and the
initial version included not only a Maven-based resolver, but also a
Gradle-based resolver too. There are some lessons I&amp;rsquo;ve learned along
the way, and maybe it would be useful to share some of them.&lt;/p&gt;
&lt;p&gt;I realise that these are particularly reflective or considered. I&amp;rsquo;ve
literally just pressed the buttons and thrown the switches for the
release, so I&amp;rsquo;ve not yet sat back and thought about things, but it&amp;rsquo;s
always nice to get some thoughts down while they&amp;rsquo;re fresh in the mind.&lt;/p&gt;
&lt;p&gt;With that said&amp;hellip;.&lt;/p&gt;
&lt;h3 id=&#34;a-little-is-better-than-nothing&#34;&gt;A little is better than nothing&lt;/h3&gt;
&lt;p&gt;It was deeply frustrating to have progress be so slow, but the thing
that helped make it bearable was being able to break the problem down
into bite-size pieces that I could work on. Being able to see slow
progress, rather than nothing at all, was a huge boon.&lt;/p&gt;
&lt;h3 id=&#34;small-tests-ftw&#34;&gt;Small tests #FTW&lt;/h3&gt;
&lt;p&gt;The way I was able to break things into bite-size pieces was to make
use of relatively fine-grained tests. These were hooked up into my
test suite and could be run at the drop of a hat, whenever a change
was made. Because of careful choices, running the entire test suite
takes about 3 seconds, which means I could run it whenever I felt like
it. Tightening the feedback loop like this was essential.&lt;/p&gt;
&lt;h3 id=&#34;software-is-collaborative-art&#34;&gt;Software is collaborative art&lt;/h3&gt;
&lt;p&gt;Along the way, I got stuck several times, and needed help figuring
things out. There were other times when I thought things were done and
I could relax. All along the way, there were fellow developers and
geeks who were happy to help. Jin and Chris, who are my
co-consipirators in the &lt;a href=&#34;https://github.com/bazelbuild/rules_jvm_external&#34;&gt;rules_jvm_external repo&lt;/a&gt;, have provided
endless patient feedback and support. &lt;a href=&#34;https://www.linkedin.com/in/tirsen/&#34;&gt;Jon Tirsen&lt;/a&gt; helped me
find bugs and issues, and even supplied patches (thank you, Jon!)&lt;/p&gt;
&lt;p&gt;Now that the software is out, I&amp;rsquo;m expecting bug reports and feature
requests to come rolling in. That&amp;rsquo;s fine. That&amp;rsquo;s half the fun, because
I know that the people asking for things will be doing so looking to
improve things.&lt;/p&gt;
&lt;h3 id=&#34;patience-is-a-virtue&#34;&gt;Patience is a virtue&lt;/h3&gt;
&lt;p&gt;Gotta be patient if you&amp;rsquo;re going to take over a year to ship a
feature!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A New Approach to CI</title>
      <link>https://www.rocketpoweredjetpants.com/2023/09/a-new-approach-to-ci/</link>
      <pubDate>Tue, 05 Sep 2023 12:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2023/09/a-new-approach-to-ci/</guid>
      <description>&lt;p&gt;In one of the groups I&amp;rsquo;m part of, someone recently asked &amp;ldquo;Does anyone
have opinions on monorepos? It seems like they should be beneficial to
CI in the same way that trunk-based development is, but I&amp;rsquo;ve never
used one in anger.&amp;rdquo; This is a edited version of my response.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a huge fan of co-locating code as a driver for &lt;a href=&#34;https://www.youtube.com/watch?v=YrgJL5SFHbM&#34;&gt;making feedback
loops tigher&lt;/a&gt;. Taking that co-location to its logical extreme,
that naturally leads you towards a monorepo. Very few people go to the
extreme, but it does suggest moving to fewer, larger repos.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a whole bunch of issues that surround larger repos, but my
view is that they can transform CI entirely. Why is that?&lt;/p&gt;
&lt;p&gt;The main question we&amp;rsquo;re trying to answer with CI is &amp;ldquo;&lt;strong&gt;is this change
safe to land into production?&lt;/strong&gt;&amp;rdquo; If we believe the change is safe, we
can push ahead. If we don&amp;rsquo;t, then we need to weigh our options, but
typically we won&amp;rsquo;t push to production.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s think about how traditional CI has attempts to answer the
question of &amp;ldquo;is this safe to land into production?&amp;rdquo; by taking a &amp;ldquo;&lt;a href=&#34;https://dictionary.cambridge.org/dictionary/english/belt-and-braces&#34;&gt;belt
and braces&lt;/a&gt;&amp;rdquo; approach: we can&amp;rsquo;t prove what has been impacted
by this change, so instead we&amp;rsquo;ll start by running a series of
pipelines to try and make sure all the bases are covered.&lt;/p&gt;
&lt;p&gt;The traditional CI pipeline looks something like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run formatters or linters that run almost instantly.&lt;/li&gt;
&lt;li&gt;Run the small tests (or unit tests, if you prefer that
terminology). This gives fast feedback.&lt;/li&gt;
&lt;li&gt;Run the medium tests (or integration tests, if you prefer)&lt;/li&gt;
&lt;li&gt;Fan out and run the large tests in buckets.&lt;/li&gt;
&lt;li&gt;Fan back in, and build artifacts for deployment.&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;li&gt;Profit!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, there are almost as many ways of creating this as there are
projects, but the general pattern is to front-load fast tests, and
then fan out to run slower tests in parallel where possible.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s notable is that we&amp;rsquo;re generally running &lt;em&gt;everything&lt;/em&gt;. That&amp;rsquo;s
because we don&amp;rsquo;t really know &lt;em&gt;what&lt;/em&gt; has been affected by a change, and
for the sake of safety, we just run everything we can, hoping that
it&amp;rsquo;ll catch any problems. For a small repo, this approach is probably
fine, but as the repo grows, it lengthens the feedback loops, and the
likelihood of something in an unrelated project to yours causing your
builds to fail increases.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;ve seen plenty of teams attempting to write sophisticated tooling to
use machine learning (or just good old fashioned statistics) to try
and figure out which tests need to be run for which change. The
results are never completely reliable, so there&amp;rsquo;s always the fallback
of running everything.&lt;/p&gt;
&lt;p&gt;The problem is that the repo is too large, and has become unwiedy to
work on with the tools we&amp;rsquo;ve grown used to. You definitely need the
right tooling to make a monorepo (or larger repo) work, and my tool of
choice at the moment is &lt;a href=&#34;https://bazel.build&#34;&gt;Bazel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not because Bazel is an amazing tool (it has a wickedly steep
learning curve, and it&amp;rsquo;s demand to completley enumerate inputs is
deeply frustrating), but that it&amp;rsquo;s great at handling larger repos in
the way that other tools just can&amp;rsquo;t, and of the new generation of
build tools out there, it&amp;rsquo;s the one with momentum (meaning that you
can find help in &lt;a href=&#34;https://stackoverflow.com/search?q=bazel&#34;&gt;Stack Overflow&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;One thing that Bazel allows is the ability to &lt;a href=&#34;https://bazel.build/query/guide&#34;&gt;query&lt;/a&gt; the build
graph. You can do some really nice things with this ability. For
example, using a tool like &lt;a href=&#34;https://github.com/bazel-contrib/target-determinator&#34;&gt;Target Determinator&lt;/a&gt;, you can identify
every single test that needs to be re-run, or library or binary that
needs to be rebuilt for each change.&lt;/p&gt;
&lt;p&gt;So your CI build stops being &amp;ldquo;run everything&amp;rdquo; and starts being &amp;ldquo;&lt;strong&gt;run
just what needs to be run&lt;/strong&gt;&amp;rdquo;, and that can save astonishing amounts of
time, if you can determine what that is reliable and at speed. At a
high level, your CI run becomes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use Target Determinator to identify the targets to rebuild and
test.&lt;/li&gt;
&lt;li&gt;Rebuild and test those targets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One nice side-effect of this is that there&amp;rsquo;s no need to keep the
entire tree green all the time. We all know that flaky tests sometimes
creep in, or a test starts failing because some external system is
down. Using target determination allows us to know that our change is
fine, even if the rest of the repo is on fire.&lt;/p&gt;
&lt;p&gt;Better yet, that irksome habit Bazel has of requiring you to list all
your inputs has the handy side-effect of making remote builds far
simpler, and not constrained to a single language (like &lt;a href=&#34;https://www.distcc.org&#34;&gt;distcc&lt;/a&gt;
does). After all the build is just taking inputs, laying them out on
disk, running a command, and collecting outputs. If those inputs are
specified in enough detail, there&amp;rsquo;s no reason to be constrained to a
single machine.&lt;/p&gt;
&lt;p&gt;Being able to do distributed builds, either on locally managed
infrastructure such as &lt;a href=&#34;https://github.com/buildbarn&#34;&gt;BuildBarn&lt;/a&gt;, or using a &amp;ldquo;build as a
service&amp;rdquo; provider such as &lt;a href=&#34;https://www.engflow.com&#34;&gt;EngFlow&lt;/a&gt; or &lt;a href=&#34;https://www.buildbuddy.io&#34;&gt;BuildBuddy&lt;/a&gt;,
is another way of tightening feedback loops by scaling the build
horizontally (though this relies on builds being broad, rather than a
single, narrow critical path) You&amp;rsquo;ve got 300 tests to run? Just run
them all at the same time. It takes the same amount of CPU, but the
wall clock time drops dramatically.&lt;/p&gt;
&lt;p&gt;Combine distributed caches, builds, and target determination, and your
CI pipeline becomes a lot easier to manage. In many cases, it will
look like the pipelines of the old days: just a straight list of steps
that are carried out in sequence, without any fan-out or fan-in.&lt;/p&gt;
&lt;p&gt;Of course, there are a host of problems that come with the approach of
using larger repos, and the two I see people get most incensed by are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single version requirements of dependencies.&lt;/li&gt;
&lt;li&gt;Having to fix other people&amp;rsquo;s builds when you break them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ve blogged a little about both of these &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2022/10/the-social-expectations-of-source-repos/&#34;&gt;here&lt;/a&gt;, and a little more about
the cost savings that monorepos can represent &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2023/06/theres-no-such-thing-as-a-free-lunch/&#34;&gt;here&lt;/a&gt;, but going into these issues in depth will need to wait for
another day.&lt;/p&gt;
&lt;p&gt;However, in short I strongly believe that monorepos are as beneficial
to CI in the same way that trunk-based development is.&lt;/p&gt;
&lt;h3 id=&#34;sotto-voce&#34;&gt;Sotto voce&lt;/h3&gt;
&lt;p&gt;I should really blog about the single version thing. It&amp;rsquo;s a pain, but
largely because it surfaces incompatibilities and makes more visible
the amount of work that needs to be done to make an update stick
everywhere it should. I liken it to how Agile used to be compared to
other methodologies (at least, how they were compared when I was at
&lt;a href=&#34;https://www.thoughtworks.com&#34;&gt;ThoughtWorks&lt;/a&gt; back before 2010).&lt;/p&gt;
&lt;p&gt;All software development projects start in a relatively chaotic way,
with uneven progress, and unforeseen hiccups. After a while, they
settle down into their own rhythms, and become more predictable. The
problem was that if a project reports progress every week or two, that
initial chaos is far more visible than something that reports progress
every month or even longer. It&amp;rsquo;s not that there&amp;rsquo;s a difference, it&amp;rsquo;s
that the visibility is far higher (we shall set aside that I hope that
most people here believe that the visibility is something that is
ultimately useful and leads to better outcomes). I think the same
applies for the single-version thing too: it surfaces
incompatibilities so much sooner, and front-loads a pile of
engineering effort that would otherwise have to be spent (with
interest!) later on in the process, where change is harder.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>There&#39;s No Such Thing as a Free Lunch</title>
      <link>https://www.rocketpoweredjetpants.com/2023/06/theres-no-such-thing-as-a-free-lunch/</link>
      <pubDate>Mon, 12 Jun 2023 20:16:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2023/06/theres-no-such-thing-as-a-free-lunch/</guid>
      <description>&lt;p&gt;One of the  things I like to talk  about with my teams is  the goal of
&lt;strong&gt;getting a new line of code  into production as quickly, as safely, and
as cheaply as possible&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The first part of the goal – getting code into production quickly – is
something every team strives for.&lt;/p&gt;
&lt;p&gt;The second part? In my experience, it tends to be eclipsed by the
first part. After all, everyone is under pressure to get the feature
they’re working on into people’s hands, and there’s always a backlog
of work to do that’s longer than the time available to do it in.&lt;/p&gt;
&lt;p&gt;That third part, about the cost? It’s very seldom considered.&lt;/p&gt;
&lt;p&gt;But for any organisation, cost is a vital part of software
development. More importantly, “cost” is something that needs to be
measured at the team, organisation, and company levels: just looking
at one isn’t enough. Allow me to explain….&lt;/p&gt;
&lt;p&gt;I like to loosely define safety as “&lt;strong&gt;confidence that a change doesn’t
break anything&lt;/strong&gt;”. That doesn’t mean that the change is entirely
perfect; after all, defects always slip through. It just means that we
have a sense of confidence that a change won’t make our systems
worse. It&amp;rsquo;s not a perfect description, but it helps to guide some of
my thinking.&lt;/p&gt;
&lt;p&gt;This definition also leaves open the question of “what can break?”
with a change.&lt;/p&gt;
&lt;p&gt;There are the obvious things that spring instantly to mind, such as
the feature not working at all, or having unforeseen and undesirable
side-effects (using too many resources, accidentally wiping data, or
being slower than is useful, for example). There are also less obvious
concerns, such as how we interact with other parts of the system, or
how changes in APIs may cause code that depends on ours to fail to
compile. If you’re using services (micro, macro, I don’t
mind. Whatever makes you happy), then the contracts between those
services are also places where we can expect – and frequently find –
breakages.&lt;/p&gt;
&lt;p&gt;The worst possible time to find out about breakages is in
production. For applications and services, what “production” means is
clear. For libraries and shared utilities, “production” may be the
point where someone else takes a dependency on that code (that is,
when someone updates the version of the library to the recently
released version and tries to recompile) Depending on how frequently
dependencies are updated, there may be a lag of months before there’s
proper confidence that a change is safe.&lt;/p&gt;
&lt;p&gt;For all changes, it’s a wise idea to depend on some level of automated
tests. Pull the update in, compile and run any &lt;a href=&#34;https://testing.googleblog.com/2010/12/test-sizes.html&#34;&gt;small tests&lt;/a&gt;, deploy
if necessary to some environment, and then run the medium and large
tests. If we’re in a single company with many repositories, it may be
possible to identify other repos that depend on the artefacts you’re
producing, and to “&lt;a href=&#34;https://publicobject.com/2023/06/10/farm-or-grind/&#34;&gt;grind and fix&lt;/a&gt;” each of them with the latest
change.&lt;/p&gt;
&lt;p&gt;So far, we’ve been considering this from a pure engineering
perspective, but now we need to don the hat of some kind of
manager-crossed-with-an-accountant, and consider costs. How do we make
sure our changes are delivered quickly, safely, and at as low a cost
as possible?&lt;/p&gt;
&lt;p&gt;Back in the Old Days, we used to talk a lot about the &lt;a href=&#34;https://www.grahambrooks.com/software-development/2010/02/01/xp-and-the-cost-of-change.html&#34;&gt;cost of change
curve&lt;/a&gt;, which posits that finding and fixing issues
earlier in the development life cycle is cheaper than doing so later
on. I think that’s an axiomatic truth, even if the exact details might
be something we can quibble over. Put another way, the longer the
feedback loop, the more expensive it is to react to the results of
that feedback loop; &lt;strong&gt;shorter feedback loops are cheaper&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With a compiled language, the earliest point we can get feedback about
a change is at compile time. Change an API, and the code won’t even
compile. Magical!&lt;/p&gt;
&lt;p&gt;The next cheapest way is to run the tests in our repo. Assuming those
tests pass, we then need to publish snapshots, and try to coordinate
changes between downstream multiple repos (maybe pulling in the
snapshot, recompiling, and running all those other tests). Of course,
each of those downstream projects need to be updated and tested in a
specific order. Your repos all have a graph of dependencies, and we
need to follow that graph, so each repo tested needs to publish more
snapshots that can be consumed further down the line, and so on, and
so on.&lt;/p&gt;
&lt;p&gt;The Apache folks tried this with &lt;a href=&#34;https://gump.apache.org&#34;&gt;Gump&lt;/a&gt;, for Ant, Maven, and
other build tools they own. Gump “builds and compiles software against
the latest development versions of those projects” It is relatively
limited in scope, but it’s already pretty complicated. It’s not a
cheap thing to do. Coordinating between the Apache projects is done on
a “best efforts” basis, rather than being something that’s mandated,
which mirrors what happens in organisations – if you identify
something that needs fixing in someone else’s repo, often you have to
report it as an issue rather than delving in to fix yourself. I’m sure
we’ve all experienced how slow that process can be.&lt;/p&gt;
&lt;p&gt;Attempting to detect and follow the graph of dependencies between
repos in a company would be challenging, especially if the
dependencies are indirect (for example, if a URL for something is
hard-coded somewhere, and that’s how the dependency between components
is expressed)&lt;/p&gt;
&lt;p&gt;The complexity and cost of building and maintaining infrastructure to
test and detect this has to be factored into the cost of making the
change. You might take a shortcut, and say that you’re only interested
in specific downstream consumers of your change, but even then,
there’s a cost to be borne, and it’s higher than making a change in a
single repo. How come? Because there’s more coordination to manage,
and longer feedback loops. As I’ve already mentioned, the inference
from the cost of change curve is that the longer feedback loop is more
expensive.&lt;/p&gt;
&lt;p&gt;In the “farm or grind” blog post, the missing first step is “find out
where the changes need to be made”. In the post, Jesse says, “you use
a combination of GitHub search, ripgrep and zoekt to find the impacted
codebases”, which sounds like something that might work for a majority
of cases, but I’m also confident that things would be missed (if, for
example, the repos weren’t public, or accessible to the person making
the change) Worse, you’ve still got to figure out the graph of
dependencies between repos to increase the safety of the change. It
ain’t cheap.&lt;/p&gt;
&lt;p&gt;So, how do we reduce the cost of building our confidence?&lt;/p&gt;
&lt;p&gt;Co-locating code helps an awful lot. Running “ripgrep and zoekt” in a
single repo is cheaper than doing so over dozens. Taken to an extreme,
this leads you to a monorepo (Yay! Monorepo! Yay!), but there may be
&lt;a href=&#34;https://www.rocketpoweredjetpants.com/2022/10/the-social-expectations-of-source-repos/&#34;&gt;perfectly sensible reasons&lt;/a&gt; why that’s impractical. In any
case, reducing the number of repos reduces the cost of a change. The
downside is that the cost of a change becomes more readily visible,
and the visibility of future pain is seldom something that excites
developers, but from the perspective of the organisation as a whole,
the cost of the change has reduced.&lt;/p&gt;
&lt;p&gt;A second strategy is to reduce the number of dependencies, and where
that’s not possible to have clear and explicit tests in each repo
which describe the contract between dependencies. Nat Pryce talks
about &lt;a href=&#34;http://www.natpryce.com/articles/000785.html&#34;&gt;simplicators&lt;/a&gt;, Eric Evans about &lt;a href=&#34;https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer&#34;&gt;anti-corruption
layers&lt;/a&gt;, and Alistair Cockburn introduced the world
to &lt;a href=&#34;https://alistair.cockburn.us/hexagonal-architecture/&#34;&gt;hexagonal architectures&lt;/a&gt;. All of these help provide
that insulation and isolation.&lt;/p&gt;
&lt;p&gt;Put another way, &lt;strong&gt;the looser the coupling between repositories in an
organisation, the cheaper a change in one is likely to be&lt;/strong&gt;, since it’s
less likely to affect the others. Conversely, tight, implicit coupling
between repositories is an argument for merging those repos — a change
in one is very likely to require a change in another, and inter-repo
testing is expensive.&lt;/p&gt;
&lt;p&gt;A third strategy is to use a modern build tool which understands the
build graph within a single repository, supports caching, and which
can identify the subset of targets that need to be built for each
change. Right now, I advocate for something like Bazel to support
this, but really any tool that properly supports caching and which
avoids unnecessary rebuilds that you and your team is happy with is a
great choice.&lt;/p&gt;
&lt;p&gt;Finally, we need to be conscious that someone needs to pay the cost of
each change. As an engineer on a team, the smaller the repository, the
cheaper the change appears to me. However, all we’ve done is
distribute, delay, and escalate the cost of validating a change
because we’ve extended the feedback to production. So, while our cost
appears reduced at the team level, the cost to the company is larger.&lt;/p&gt;
&lt;p&gt;Worse, I don’t believe that the cost to the team is really as small as
we believe. Bug reports coming in from other teams many months after a
feature has landed are later on the cost of change curve, and so more
expensive to fix. Worse, the context for a change is no longer readily
available, so it takes extra engineering effort to properly respond to
those bug reports and requests to change when they do eventually
arrive.&lt;/p&gt;
&lt;p&gt;Does this all mean that a single repository with everything in it is
“cheap”? Absolutely not. It’s astonishingly expensive, and the tooling
required is cutting edge. However, the alternative is more complex,
requires an array of tooling that doesn’t even exist yet, and has
lengthed feedback loops. It’s definitely not cheaper in the long run.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Your Roots Are Showing</title>
      <link>https://www.rocketpoweredjetpants.com/2023/05/your-roots-are-showing/</link>
      <pubDate>Wed, 10 May 2023 20:16:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2023/05/your-roots-are-showing/</guid>
      <description>&lt;p&gt;To know where we&amp;rsquo;re going, it helps to know where we&amp;rsquo;ve come
from. While this is true for ourselves, it&amp;rsquo;s also true of software. It
helps explain why some of the things that look unusual or unlikely
have been done that way, and helps demonstrate some of the forces that
have acted on the design of an apps UI and UX.&lt;/p&gt;
&lt;p&gt;Why do I mention this? Because when I introduce people to
&lt;a href=&#34;https://bazel.build/&#34;&gt;Bazel&lt;/a&gt; I find it helpful to explain where the tool came from
in order to understand why it has the UI that it does. A lot of this
is hinted at in the &lt;a href=&#34;https://mike-bland.com/2012/10/01/tools.html&#34;&gt;post that Mike Bland wrote over 10 years
ago&lt;/a&gt;, but perhaps now is the time to flesh out the story a
little more. I&amp;rsquo;d suggest going to read his post before carrying on
here. It&amp;rsquo;s an interesting read, and we&amp;rsquo;re not in a rush. Go for it.&lt;/p&gt;
&lt;p&gt;The first place where Bazel&amp;rsquo;s roots are showing is in its own
name. Looking at the source of Bazel, it should come as no surprise
that it&amp;rsquo;s derived from &lt;code&gt;blaze&lt;/code&gt;: Google&amp;rsquo;s own build tool. Indeed,
&amp;ldquo;bazel&amp;rdquo; is an anagram of &amp;ldquo;blaze&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;blaze&lt;/code&gt; wasn&amp;rsquo;t created in a vacuum. When it was introduced at
Google, it replaced the older build system, which relied on a two-step
process to perform a build. The first step was to run a tool that took
build files and converted them into a &lt;code&gt;Makefile&lt;/code&gt;. The second step was
to run the build itself.&lt;/p&gt;
&lt;p&gt;The build files were an amazing abstraction. Rather than describing
the individual steps required to build an artifact, they simply
described the kinds of artifacts to be built. If you saw one now, it
would feel remarkably familiar. Without that abstraction, I&amp;rsquo;m not sure
how easy it would have been to keep growing the Google monorepo.&lt;/p&gt;
&lt;p&gt;The build files needed to be written in a language of some sort, and
at Google there were (notoriously) four &amp;ldquo;blessed&amp;rdquo; languages for
writing code: C++ for performance critical code and if you liked it,
Java for other server-side code, JS because that&amp;rsquo;s what you needed to
run in a browser, and Python for everything else. Clearly, the
sensible choice from this list if you needed a programmatic way of
describing your build was Python.&lt;/p&gt;
&lt;p&gt;And, sure enough, originally the build files were interpreted using
Python.&lt;/p&gt;
&lt;p&gt;As a little historical note, this is the same approach we took when we
were developing &lt;a href=&#34;https://buck.build/&#34;&gt;Buck&lt;/a&gt; too, and that shouldn&amp;rsquo;t come as a
surprise since the team working on Buck were largely part of the
Xoogler diaspora. But I digress&amp;hellip;.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s one huge problem with interpreting user-supplied
build files written in fragments of Python in a build tool that&amp;rsquo;s
meant to be deterministic and reproducible: you can do just about
anything, including futzing with the file system, or reaching out to
network resources. Worse, there was no way being able to determine
whether &amp;ldquo;parsing&amp;rdquo; the build files would ever finish, or could be done
without undue computational load on the machine doing the build.&lt;/p&gt;
&lt;p&gt;So, it was decided that it was better to use a tightly constrained
subset of Python. By providing a different interpreter, it would be
possible to avoid accidentally relying on modules that were only
installed on a handful of machines. It would also be possible to prove
that parsing the build files would complete (yay! No &lt;a href=&#34;https://en.wikipedia.org/wiki/Halting_problem&#34;&gt;halting
problem&lt;/a&gt;!)&lt;/p&gt;
&lt;p&gt;And if you go and read the goals of &lt;a href=&#34;https://github.com/bazelbuild/starlark&#34;&gt;Starlark&lt;/a&gt;, you&amp;rsquo;ll see
that this is exactly what happened. Put another way, Starlark is
another place where the roots of Bazel shine through — it looks
like Python because at one stage is &lt;em&gt;was&lt;/em&gt; Python, and it was simpler
to slowly tighten the constraints of what was allowed in build files
over time than to rewrite every build file in the whole of Google&amp;rsquo;s
monorepo. Fortunately, originally most of the build files weren&amp;rsquo;t
doing anything fancy to begin with, and so could be interpreted using
this new subset of Python.&lt;/p&gt;
&lt;p&gt;But we&amp;rsquo;re not done yet! There&amp;rsquo;s one other thing that Mike mentions in
his post that is pertinent to this discussion of Bazel&amp;rsquo;s past leaking
into its UX, and that is that Google used &lt;a href=&#34;https://www.perforce.com/manuals/p4guide/Content/P4Guide/streams.concepts.paths.inherit.html&#34;&gt;Perforce&lt;/a&gt; for
source control.&lt;/p&gt;
&lt;p&gt;Now, if you&amp;rsquo;re been fortunate enough to be introduced to source
control in the modern age, you may not be aware of just how many
source control systems there used to be. In the Open Source world, the
move from &lt;a href=&#34;https://en.wikipedia.org/wiki/Revision_Control_System&#34;&gt;RCS&lt;/a&gt; to &lt;a href=&#34;https://www.gnu.org/software/trans-coord/manual/cvs/cvs.html&#34;&gt;CVS&lt;/a&gt; allowed us to group changes to
multiple files into a single commit. The move from CVS to
&lt;a href=&#34;https://subversion.apache.org&#34;&gt;Subversion&lt;/a&gt; made those commits atomic (prior to Subversion, if
two people used CVS to commit a change at the same time, it was
possible for two separate commits to get the same revision number, and
that lead to plenty of hilarity).&lt;/p&gt;
&lt;p&gt;But there weren&amp;rsquo;t just Open Source source control systems out
there. For example, the well-known falling out of the Linux Kernal
devlopers with &lt;a href=&#34;https://en.wikipedia.org/wiki/BitKeeper&#34;&gt;BitKeeper&lt;/a&gt; lead Linus Torvalds to create
&lt;a href=&#34;https://git-scm.com&#34;&gt;git&lt;/a&gt;, and which also lead to the creation of &lt;a href=&#34;https://www.mercurial-scm.org&#34;&gt;mercurial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But not all source control tools are, or were, Open Source. There were
many commercial ones, and Google had settled on Perforce, which had a
reputation for being flexible, fast, and capable.&lt;/p&gt;
&lt;p&gt;The way that Perforce works is that you create a Perforce client. This
is done by specifying paths within the repo that you want to check
out, and then run the &lt;code&gt;p4&lt;/code&gt; tool to get everything dragged down from
the Perforce server to your local disk.&lt;/p&gt;
&lt;p&gt;These paths will look familiar to anyone who&amp;rsquo;s used Bazel because they
look exactly like the label syntax that is used for specifying
targets, &lt;code&gt;//they/look/like/this/...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The original tooling at Google took advantage of this by providing a
utility that allowed a developer to clone a minimal but sufficient
part of the larger monorepo. They did this by specifying the build
targets to build, finding the relevant build files by converting build
target paths to the perforce equivalents (a trivial transformation),
and then parsing those to extract more paths, and so on, until you had
everything you needed.&lt;/p&gt;
&lt;p&gt;So, this has been a lot of words to describe three places where
Bazel&amp;rsquo;s history have leaked into its current incarnation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The name is an anagram of &lt;code&gt;blaze&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Starlark looks like Python because it once &lt;em&gt;was&lt;/em&gt; Python.&lt;/li&gt;
&lt;li&gt;Bazel labels look like Perforce paths because they were originally
Perforce paths&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>The Social Expectations of Source Repos</title>
      <link>https://www.rocketpoweredjetpants.com/2022/10/the-social-expectations-of-source-repos/</link>
      <pubDate>Mon, 24 Oct 2022 12:53:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2022/10/the-social-expectations-of-source-repos/</guid>
      <description>&lt;p&gt;I have Keanu Reeves to thank for one of my favourite life mottos: &lt;a href=&#34;https://www.youtube.com/watch?v=rph_1DODXDU&#34;&gt;Be
excellent to each
other&lt;/a&gt;. It seems to be
something that I can apply so often, in so many ways.&lt;/p&gt;
&lt;p&gt;Take &amp;ldquo;working with other people on source code&amp;rdquo; for example.&lt;/p&gt;
&lt;p&gt;For the sake of this post, I&amp;rsquo;m talking about large repository with
many people working semi-independently on it. Those are most often
found in companies, but there are some examples in the world of Open
Source.&lt;/p&gt;
&lt;h3 id=&#34;setting-the-scene&#34;&gt;Setting the scene&lt;/h3&gt;
&lt;p&gt;There are always social expectations anywhere you have groups of
people attempting to collaborate and get along with one another. You
and whoever you live with? There are social expectations. You and the
people you work with? There are social expectations. You and whoever
you share a code repository with? There are social expectations.&lt;/p&gt;
&lt;p&gt;One of the things that I&amp;rsquo;ve noticed is that people seldom think about
the social expectations of the repos they inhabit. And I guess that&amp;rsquo;s
only natural.&lt;/p&gt;
&lt;p&gt;Maybe we don&amp;rsquo;t share our repo with anyone else. What we say should
happen, happens, because we&amp;rsquo;re the only ones that are impacted by it.&lt;/p&gt;
&lt;p&gt;For some of us, we share a repo with only a few people, and we tend to
work fairly closely with them. It&amp;rsquo;s normally pretty easy to come to an
agreement on whatever the hot-button contentious issue of the day is.&lt;/p&gt;
&lt;p&gt;But, as teams grow, and the repository becomes larger, we end up at a
point where we don&amp;rsquo;t necessarily know all the people. You&amp;rsquo;ll see
things like &lt;a href=&#34;https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners&#34;&gt;code
owners&lt;/a&gt;
files appearing at this point, and the number of people who can commit
at the root of the repository tends to be dramatically reduced.&lt;/p&gt;
&lt;p&gt;And this is where we should be excellent to each other.&lt;/p&gt;
&lt;p&gt;But, if we have the freedom to do whatever we want in a small repo,
why would we possibly want to be in a larger one, let alone taking
things to the extreme of a monorepo?&lt;/p&gt;
&lt;h3 id=&#34;the-more-the-merrier&#34;&gt;The more, the merrier&lt;/h3&gt;
&lt;p&gt;Well, for the same reason that living in cities is so popular: for the
trade-off of some freedom and some additional costs, there are a whole
heap of advantages, and these come from the scale of what you&amp;rsquo;re
sharing.&lt;/p&gt;
&lt;p&gt;For example, let&amp;rsquo;s take the classic bugbear of anyone working in a
large repo: updating shared dependencies.&lt;/p&gt;
&lt;p&gt;You might think thay updating a dependency in a small repo isn&amp;rsquo;t
really that much work, and most of the time you&amp;rsquo;d be right. Someone
gets assigned the task to do the update, they do the work, fix any
issues, and they&amp;rsquo;re done. Simple.&lt;/p&gt;
&lt;p&gt;Except, when you&amp;rsquo;re doing large scale programming, it&amp;rsquo;s not just one
repo. There might be dozens (hundreds!) of repos. For each of those
repos, an engineer must handle the update. Cumalatively, &lt;strong&gt;the
aggregated engineering hours required to perform the update tends to
be higher&lt;/strong&gt; than changing a similar number of projects in a larger
repo.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s all to do with context &amp;mdash; if the update doesn&amp;rsquo;t cause
any problems, and there are no weird bugs, then the cumulative effort
of getting one engineer in one place to do one update is clearly lower
than the cost of gettings dozens of engineers to do the same work
across multiple repos. When things go wrong, the experience gained
fixing one project can be applied to others in the same repo.&lt;/p&gt;
&lt;p&gt;That is, in the best case scenario, the larger repo is cheaper
&lt;strong&gt;overall&lt;/strong&gt; to update than the smaller repos, even if it requires more
work for the engineer doing the heavy lifting.&lt;/p&gt;
&lt;p&gt;Put another way, &lt;strong&gt;smaller repos optimise for the micro-case. Larger
repos allow optimisations for the macro-case&lt;/strong&gt;. When you have limited
engineering capacity and many projects, it most often makes sense to
optimise for the macro-case.&lt;/p&gt;
&lt;p&gt;But no repo is an island. We deploy our software. At some point, our
code will need to integrate with someone else&amp;rsquo;s code. It&amp;rsquo;s only then
that we find out our assumptions about how other projects will act are
right or wrong.&lt;/p&gt;
&lt;p&gt;With a small repo, we can iterate quickly. We can do so because we
delay the point at which we integrate with others. But! It&amp;rsquo;s a truism
in software development that &lt;a href=&#34;https://www.pmi.org/disciplined-agile/agile/costofchange&#34;&gt;the later a defect is found, the more
expensive it is to
fix&lt;/a&gt;. By
delaying the point of integration, we&amp;rsquo;ve increased the cost of fixing
any integration.&lt;/p&gt;
&lt;p&gt;OTOH, while it&amp;rsquo;s deeply frustrating for someone working in a larger
repo to find out that they&amp;rsquo;ve broken something, we&amp;rsquo;re front-loading
the cost of integration. Intuitively, this means that the &lt;strong&gt;overall&lt;/strong&gt;
cost of this integration will be lower.&lt;/p&gt;
&lt;p&gt;Put another way, &lt;strong&gt;smaller repos optimise for local changes, trading
that for increased integration costs. Larger repos optimise for
reduced overall integration costs, trading that for more effort being
required to land a single change in the tree&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And then, there&amp;rsquo;s the costs of CI and build infrastructure. Unless a
repo is particularly simple, there is likely to be some kind of build
process, and some kind of CI pipeline. As the amount of code grows,
the CI pipelines may slow down. We tend to end up with someone being
assigned to &amp;ldquo;make the build faster&amp;rdquo;, or with dedicated build
engineers.&lt;/p&gt;
&lt;p&gt;Again, with many small repos, the individual costs may not be high
(after all, these repos tend to be simpler by definition), but
aggregated over an entire organisation, the total engineering cost
tends to be higher. Larger repos can aggregate this cost into experts
in the build process, who can focus on improving feedback loops for
significantly more engineers.&lt;/p&gt;
&lt;p&gt;Put another way, &lt;strong&gt;we can view the build as the fulcrum on which we&amp;rsquo;re
trying to move the world&lt;/strong&gt;. The larger the codebase, the longer our
lever, and the more impact an improvement can have on people.&lt;/p&gt;
&lt;p&gt;And these are only some of the ways that working in a larger codebase
can be more efficient than working with the same amount of code spread
across multiple repos.&lt;/p&gt;
&lt;h3 id=&#34;but-it-hurts&#34;&gt;But it hurts&lt;/h3&gt;
&lt;p&gt;There are major downsides to large repos: some technical, some
social. I clearly have an agenda, but it would be wrong to ignore them
entirely 😁&lt;/p&gt;
&lt;p&gt;Technically, larger repos need a build tool that can cope with large
repos. They take longer to clone. Thought needs to be put into making
CI processes more efficient for repos with multiple projects, and not
all CI tools are set up for this challenge. But the build tool is the
kicker: over a certain size, most tools end up weeping gently and not
working well at all. That&amp;rsquo;s one of the reasons I&amp;rsquo;m a fan of tools such
as &lt;a href=&#34;https://bazel.build&#34;&gt;Bazel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But the social issues are the important ones. Most of us have only
ever spent time in smaller repos. &lt;strong&gt;It chafes and hurts to have to
meet the social contract of working in a larger repo&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We want to update a dependency for ourselves: it seems like wasted
effort on our part to have to upgrade everyone else. And, guess what?
You&amp;rsquo;re right! It is more effort &lt;strong&gt;for you&lt;/strong&gt; to do that update for
everyone, but it&amp;rsquo;s still more efficient overall. And it ignores all
the times when someone else has jumped through the upgrade hoops and
silently updated something you depend on.&lt;/p&gt;
&lt;p&gt;Our PR takes longer to land because the CI build tells us some project
we know nothing about now has failing tests. What a pain! I don&amp;rsquo;t care
about them! Figuring out what&amp;rsquo;s wrong with their code is slowing me
down! And, guess what? You&amp;rsquo;re right! It is slowing &lt;strong&gt;you&lt;/strong&gt; down. But,
here&amp;rsquo;s the thing, overall it&amp;rsquo;s more efficient. You have the context
for your change right there in your mind. While fixing a failing build
or test is seldom fun, it&amp;rsquo;s still easier to fix it with better
context. So, yes, it&amp;rsquo;s slower for you, but it&amp;rsquo;s faster overall.&lt;/p&gt;
&lt;h3 id=&#34;a-brief-discussion-about-updating-shared-dependencies&#34;&gt;A brief discussion about updating shared dependencies&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s worth spending a bit of time thinking about updating
dependencies. When talking to people who are sceptical about working
in a large repo, this is normally presented as the number one problem
to solve.&lt;/p&gt;
&lt;p&gt;One argument against shared repos is that the difficulty of making
dependency updates can lead to the ossification of the repo. Imagine
someone wants to try an experiment in production with some fancy new
library, but pulling this in will mean that a transitive dependency
needs to be updated, and this causes some other service that they
don&amp;rsquo;t care about to need work done. Do you try the experiment, or not?
It takes engineering effort to do this work, and because the cost is
higher in a shared repo, the possibility that the investigation isn&amp;rsquo;t
worth that effort is higher.&lt;/p&gt;
&lt;p&gt;So this is a good time to have a thoughtful conversation about the
risk/reward trade-off that needs to be made. If I were working on
this, the first thing I&amp;rsquo;d do is a quick spike to see if the work was
as complicated as I feared.&lt;/p&gt;
&lt;p&gt;This is why most large repos I&amp;rsquo;ve seen have had a mechanism in place
to allow multiple versions of the same dependency to live in the same
repo &lt;em&gt;for a very short period of time&lt;/em&gt;, or allow people to release
experiments from a relatively short-lived branch. In both of these
cases, the choice is made thoughtfully and carefully. Yes, it does
mean that sometimes less experimentation happens, but the question
remains of the value of those experiments.&lt;/p&gt;
&lt;p&gt;The other argument aginst a shared repo when discussing updating deps
is what to do when you can&amp;rsquo;t clean up someone else&amp;rsquo;s code because you
don&amp;rsquo;t have enough context about how it works. There&amp;rsquo;s a simple answer
to this, but it&amp;rsquo;s not one that goes down very well: have &lt;em&gt;A
Conversation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Now, this implies that the social contract in a large repo is that
teams are aware that they&amp;rsquo;re sharing the repo with others, and they&amp;rsquo;re
willing to be good neighbours. If someone comes to you asking for help
in a part of the tree that you&amp;rsquo;re familiar with, then offering the
help they need is a neighbourly thing to do.&lt;/p&gt;
&lt;p&gt;Some people may recoil from this because they don&amp;rsquo;t like talking to
people, and that&amp;rsquo;s unfortunate. Other people may shy away from having
a conversation because they know the team they want to talk to has
absolutely no capacity or time to help them. I&amp;rsquo;d suggest that running
a team ragged like this isn&amp;rsquo;t necessarily in the best interests of the
long-term health of the codebase. Another reason not to have this
conversation is because the work culture precludes this, for whatever
reason. If that&amp;rsquo;s the case, then the social pressures against having a
small number of large repos will cause fragmentation and separation
into smaller repos, no matter the engineering costs.&lt;/p&gt;
&lt;p&gt;Social considerations almost always end up trumping technical
concerns.&lt;/p&gt;
&lt;p&gt;As a final note, I&amp;rsquo;ve observed that dependency updates (no matter the
size of the repo) tend to be bi-modal: most tend to be pretty easy and
straight-forward, but some turn out to be absolute monsters. At some
point, I should blog some strategies for dealing with these.&lt;/p&gt;
&lt;h3 id=&#34;choose-what-to-optimise-for&#34;&gt;Choose what to optimise for&lt;/h3&gt;
&lt;p&gt;Really, the social contract of a larger repo is that you accept that
there will be times where what you want to do is slower and more
difficult, because overall that discomfort will lead to reduced effort
overall.&lt;/p&gt;
&lt;p&gt;Conversely, the social contract of a smaller repo is that we&amp;rsquo;re
optimising for our smaller team&amp;rsquo;s comfort, at the price of higher
integration costs, and needing to be responsible for all updates to
our dependencies ourselves.&lt;/p&gt;
&lt;p&gt;Which really means that you have a choice: do you optimise for the
smaller or larger repo? Do you choose to spend more engineering effort
overall in a less visible way, or do you spend less engineering effort
overall, but because integration happens sooner, in a more visible
way?&lt;/p&gt;
&lt;p&gt;To me, it&amp;rsquo;s obvious which approach I&amp;rsquo;d pick in almost all cases.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>bzlmod Modules</title>
      <link>https://www.rocketpoweredjetpants.com/2022/01/bzlmod-modules/</link>
      <pubDate>Mon, 24 Jan 2022 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2022/01/bzlmod-modules/</guid>
      <description>&lt;p&gt;Recently, &lt;a href=&#34;https://github.com/bazelbuild/bazel/releases/tag/5.0.0&#34;&gt;Bazel 5&lt;/a&gt; was released. Hidden behind a flag is the
new &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/bzlmod.html&#34;&gt;bzlmod&lt;/a&gt; tool. This is effectively a package manager for Bazel
rulesets that&amp;rsquo;s baked into Bazel itself, the goal being to replace
complicated &lt;code&gt;WORKSPACE&lt;/code&gt; stanzas with a simple and purely declarative
model. Ultimately, there should be no need for a workspace file at
all.&lt;/p&gt;
&lt;p&gt;At work, we maintain a relatively complicated suite of choreographed
rulesets, and simplifying maintaining that seemed like too good an
opportunity to miss, so I dove in to figure out how to get the most
from &lt;code&gt;bzlmod&lt;/code&gt;. Here&amp;rsquo;s what I learnt.&lt;/p&gt;
&lt;h3 id=&#34;quick-overview&#34;&gt;Quick Overview&lt;/h3&gt;
&lt;p&gt;Although &lt;code&gt;bzlmod&lt;/code&gt; is actually baked into Bazel itself and not a
standalone tool, I still refer to it by a separate name because that&amp;rsquo;s
how my brain works. If that confuses you, I apologise. If it helps,
you can think of &lt;code&gt;bzlmod&lt;/code&gt; as the tool that does dependency resolution
for the rulesets you&amp;rsquo;re using before it hands off the build to Bazel
&amp;ldquo;proper&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bzlmod&lt;/code&gt; reads a &lt;code&gt;MODULE.bazel&lt;/code&gt; file. To begin with, this is
relatively simple, consisting of an opening call to a &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#module&#34;&gt;module&lt;/a&gt;
function, and then a series of calls to &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#bazel_dep&#34;&gt;bazel_dep&lt;/a&gt; to declare a
dependency on another ruleset.&lt;/p&gt;
&lt;p&gt;At resolution time, &lt;code&gt;bzlmod&lt;/code&gt; will check in the &lt;a href=&#34;https://github.com/bazelbuild/bazel-central-registry&#34;&gt;bazel central
registry&lt;/a&gt; for the lowest version of each dependency declared
within the &lt;code&gt;MODULE.bazel&lt;/code&gt; file (much like Go does). The nice thing
here is that each module (including your project!) need only declare
its first order dependencies. That&amp;rsquo;s different from the approach taken
in the regular &lt;code&gt;WORKSPACE&lt;/code&gt;-based projects, where you&amp;rsquo;re responsible
for ensuring that the transitive deps of the rulesets you use are also
loaded.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re on a corporate network or don&amp;rsquo;t want to depend on the
regular central registry, you can override the location of the
registry by using the &lt;code&gt;--registry&lt;/code&gt; flag. This takes a URL as its
argument, and that URL can be a &lt;code&gt;file://&lt;/code&gt; URL.&lt;/p&gt;
&lt;p&gt;Just as regular rulesets can declare repository rules, &lt;code&gt;bzlmod&lt;/code&gt;
modules can declare &amp;ldquo;extensions&amp;rdquo;. These are regular &lt;code&gt;bzl&lt;/code&gt; files,
written in Starlark, that contain a combination of &amp;ldquo;tags&amp;rdquo; and
&lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#module_extension&#34;&gt;module_extension&lt;/a&gt;s. They&amp;rsquo;re loaded using a call to
&lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#use_extension&#34;&gt;use_extension&lt;/a&gt; More on this later!&lt;/p&gt;
&lt;p&gt;One consequence of this design is that a ruleset can be both a regular
&amp;ldquo;workspace&amp;rdquo;-based ruleset, as well as a module.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m calling the process of converting a ruleset to be a &lt;code&gt;bzlmod&lt;/code&gt;
module &amp;ldquo;modularisation&amp;rdquo; (that&amp;rsquo;s with a &amp;ldquo;z&amp;rdquo; in the middle if you&amp;rsquo;re
using US English 😀)&lt;/p&gt;
&lt;h3 id=&#34;preparing-for-bzlmod&#34;&gt;Preparing for &lt;code&gt;bzlmod&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;By default, &lt;code&gt;bzlmod&lt;/code&gt; isn&amp;rsquo;t enabled. To opt into using it, the
following needs to be added to a project&amp;rsquo;s &lt;code&gt;.bazelrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;common --experimental_enable_bzlmod
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, of course, you should pin the repo to using Bazel 5 or above. If
you&amp;rsquo;re using &lt;a href=&#34;https://github.com/bazelbuild/bazelisk&#34;&gt;bazelisk&lt;/a&gt; this is as easy as
&lt;code&gt;echo 5.0.0 &amp;gt;.bazelversion&lt;/code&gt;. If you&amp;rsquo;re reading this in the future (one hopes you
are), then just use the version of Bazel that&amp;rsquo;s current at the moment.&lt;/p&gt;
&lt;h3 id=&#34;the-wrong-module-development-workflow&#34;&gt;The Wrong Module Development Workflow&lt;/h3&gt;
&lt;p&gt;My original attempt to work with modules involved making a local clone
of the &lt;a href=&#34;https://github.com/bazelbuild/bazel-central-registry&#34;&gt;bazel central registry&lt;/a&gt;, and adding the ruleset I wanted
to modularise by running the &lt;code&gt;//tools:add_module.py&lt;/code&gt; script (by hand:
there&amp;rsquo;s no bazel build file here, and you may need to install some
python dependencies to get the thing to work)&lt;/p&gt;
&lt;p&gt;Once the module was added, I created a fork in the ruleset I wanted to
modularise, created a branch in that and pushed to a private GitHub
repo. That&amp;rsquo;s because I&amp;rsquo;d used the branch&amp;rsquo;s URL as the location of the
module when calling &lt;code&gt;add_module.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Within the Central Registry clone, there&amp;rsquo;s an integrity code. This is
a base64 encoded sha256, and every update to the ruleset needs to also
be matched with an update to that integrity code.&lt;/p&gt;
&lt;p&gt;My workflow was therefore:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a local change to my fork.&lt;/li&gt;
&lt;li&gt;Commit the change and push to the GitHub repo&lt;/li&gt;
&lt;li&gt;Update the integrity code in the Central Registry clone&lt;/li&gt;
&lt;li&gt;Kill the running &lt;code&gt;bazel&lt;/code&gt; instance in the project that uses the
module I&amp;rsquo;m working on because Bazel stores the resolution.&lt;/li&gt;
&lt;li&gt;Attempt to use the change, find a typo, go back to step 2.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Needless to say, this process was slow and very tedious.&lt;/p&gt;
&lt;h3 id=&#34;the-right-module-development-workflow&#34;&gt;The Right Module Development Workflow&lt;/h3&gt;
&lt;p&gt;What I &lt;em&gt;should&lt;/em&gt; have done is modify the &lt;code&gt;MODULE.bazel&lt;/code&gt; file in the
project I was working on that used the ruleset I was modularising by
adding a stanza like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-starlark&#34; data-lang=&#34;starlark&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# I was working on `rules_jvm_external`. This version hasn&amp;#39;t been&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# released yet!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bazel_dep(name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rules_jvm_external&amp;#34;&lt;/span&gt;, version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5.0.0&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# And then later&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;local_path_override(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    module_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rules_jvm_external&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;# matches the name of the `bazel_dep`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;../path/to/my/clone/of/rules_jvm_external&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, every time I made a change in the ruleset I was modularising
(&lt;code&gt;rules_jvm_external&lt;/code&gt; in this case), it was picked up automatically,
without needing to restart the bazel daemon. This sped up development
an awful lot.&lt;/p&gt;
&lt;p&gt;The only caveat with this approach is that the &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#local_path_override&#34;&gt;local_path_override&lt;/a&gt;
only works in the &amp;ldquo;top level&amp;rdquo; project. That is, while the module
override works in the project that&amp;rsquo;s importing the modularised
ruleset, if there was a similar call in &lt;em&gt;that&lt;/em&gt;, it would be ignored.&lt;/p&gt;
&lt;h3 id=&#34;tags-are-strongly-typed-macros&#34;&gt;Tags are Strongly Typed Macros&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take an example from &lt;code&gt;rules_jvm_external&lt;/code&gt; in a workspace-based project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-starlark&#34; data-lang=&#34;starlark&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;maven_install(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    artifacts &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        maven&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;artifact(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            group &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;com.google.guava&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            artifact &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;guava&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;27.0-android&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            exclusions &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;junit:junit:4.12&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    repositories &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://repo1.maven.org/maven2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The way to handle this in a &amp;ldquo;MODULE.bazel&amp;rdquo; is to use
&lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#tag_class&#34;&gt;tags&lt;/a&gt;. These are like stripped down rules, in that they
have no imeplementation function, but they do have a set of &lt;code&gt;attrs&lt;/code&gt;,
each of which are defined as being one of the entires in the &lt;a href=&#34;https://docs.bazel.build/versions/main/skylark/lib/attr.html&#34;&gt;attr&lt;/a&gt;
module.&lt;/p&gt;
&lt;p&gt;One limitation of the &lt;code&gt;MODULE.bazel&lt;/code&gt; file is that you&amp;rsquo;re not allowed
to use functions or &lt;code&gt;load&lt;/code&gt; an external resource. This means that you
can&amp;rsquo;t use macros in the way that we&amp;rsquo;re used to, and this caused me
some serious head-scratching. Fortunately, after a conversation with
Xudong Yang, it became clear there was another way to think about this
problem.&lt;/p&gt;
&lt;p&gt;The trick is that &lt;code&gt;bzlmod&lt;/code&gt; will agregate all the tags defined
transitively in a module, and the module implementation function can
iterate over them. That means that the above stanza in a
&lt;code&gt;MODULE.bazel&lt;/code&gt; could be written as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-starlark&#34; data-lang=&#34;starlark&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;maven&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;install(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    artifacts &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;junit:junit:4.12&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    repositories &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://repo1.maven.org/maven2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;maven&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;artifact(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    group &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;com.google.guava&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    artifact &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;guava&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;27.0-android&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    exclusions &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are two things to note here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;maven&lt;/code&gt; is the value returned by &lt;code&gt;module_extension&lt;/code&gt;, and both
&lt;code&gt;install&lt;/code&gt; and &lt;code&gt;artifact&lt;/code&gt; are both tags classes.&lt;/li&gt;
&lt;li&gt;The module extension&amp;rsquo;s implementation function aggregates the data
from both of these into a single data structure, which is then
resolved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While this allows &lt;code&gt;rules_jvm_external&lt;/code&gt; to express what needs to be
said, it&amp;rsquo;s pretty clear that if the &lt;code&gt;artifact&lt;/code&gt; tag needed a macro
itself, we&amp;rsquo;d rapidly be in a whole world of pain. Fortunately, in my
case, we don&amp;rsquo;t, so that&amp;rsquo;s fine 😀&lt;/p&gt;
&lt;h3 id=&#34;module-implementation-functions-replace-workspace-stanzas&#34;&gt;Module Implementation Functions Replace Workspace Stanzas&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#module_extension&#34;&gt;module_extension&lt;/a&gt;&amp;rsquo;s implementation function gets access to a pretty
anaemic &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/module_ctx.html&#34;&gt;module_ctx&lt;/a&gt;. This would be a problem, but the
implementation function is free to call as many &lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#repository_rule(implementation,%20attrs,%20local,%20environ,%20configure,%20remotable,%20doc)&#34;&gt;repository_rule&lt;/a&gt;s
as it wants to. You can also rely on the rulesets declared as a
&lt;a href=&#34;https://docs.bazel.build/versions/5.0.0/skylark/lib/globals.html#bazel_dep&#34;&gt;bazel_dep&lt;/a&gt; to be present.&lt;/p&gt;
&lt;p&gt;This allows the module implementation function to effectively contain
the bulk of what would normally be in the stanzas of code that get
added to a workspace file.&lt;/p&gt;
&lt;p&gt;Care must be taken to avoid the need to call &lt;code&gt;load&lt;/code&gt; in the
implementation function: although the implementation is a lot like a
subset of a workspace file, it&amp;rsquo;s not exactly the same. In the case of
what I wanted to do for &lt;code&gt;rules_jvm_external&lt;/code&gt;, this means that the lock
file gets parsed at least twice: once so that I can generate a series
of &lt;code&gt;http_file&lt;/code&gt; dependencies, and once so that the actual &lt;code&gt;@maven&lt;/code&gt;
workspace can be set up.&lt;/p&gt;
&lt;h3 id=&#34;bzlmod-lock-files&#34;&gt;&lt;code&gt;bzlmod&lt;/code&gt; Lock Files&lt;/h3&gt;
&lt;p&gt;At some point in the future, &lt;code&gt;bzlmod&lt;/code&gt; will have &lt;a href=&#34;https://github.com/bazelbuild/bazel/issues/14554&#34;&gt;its own lock
file&lt;/a&gt;. I&amp;rsquo;m not entirely sure what this will look like, but
my belief is that this will be structured in such a way that your
module implementation function should only be called when one of its
inputs (including attributes of tags) has changed. In the case of
&lt;code&gt;rules_jvm_external&lt;/code&gt;, this will most likely be when the artifacts
being imported into your project changes.&lt;/p&gt;
&lt;p&gt;This will be incredibly useful. One of the painful things when working
with workspace files is waiting for all the transitive deps to
download and be set up so Bazel can figure out which bits of them it
needs to use. On larger projects, this can take a long time. Being
able to start building faster, can only be a Good Thing, and I look
forward to it!&lt;/p&gt;
&lt;h3 id=&#34;managing-your-modulebazel&#34;&gt;Managing Your &lt;code&gt;MODULE.bazel&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Right now, you can&amp;rsquo;t. Because there&amp;rsquo;s no way of calling &lt;code&gt;load&lt;/code&gt; in a
&lt;code&gt;MODULE.bazel&lt;/code&gt; file, there&amp;rsquo;s no way to segment the thing in a
meaningful way. I&amp;rsquo;ve raised an &lt;a href=&#34;https://github.com/bazelbuild/bazel/issues/14632&#34;&gt;issue&lt;/a&gt; to do with this, and I
know it&amp;rsquo;s something the Bazel developers are aware of, so I hope that
once &lt;code&gt;bzlmod&lt;/code&gt; is no longer hidden behind a flag, this will be
possible.&lt;/p&gt;
&lt;h3 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Overall, after kicking the tyres and trying it out, I think that I
like &lt;code&gt;bzlmod&lt;/code&gt;, and it&amp;rsquo;ll be fun to see how it grows and changes,
especially as rulesets migrate to using it.&lt;/p&gt;
&lt;p&gt;Right now, it&amp;rsquo;s usable, but there are some corner cases where it&amp;rsquo;s not
quite there yet (notably when a module declares a dependency on a
repository via a generated build file) Having seen how quickly the
Bazel team have leaped on the issues I&amp;rsquo;ve filed, I&amp;rsquo;m very confident
that problem will be resolved.&lt;/p&gt;
&lt;p&gt;My advice? Try migrating your ruleset to &lt;code&gt;bzlmod&lt;/code&gt;, and see what works
for you and what doesn&amp;rsquo;t. I suspect there&amp;rsquo;s enough there for it to
work just as you&amp;rsquo;d expect in many cases.&lt;/p&gt;
&lt;p&gt;My thanks to the Bazel developers, Xudong Yang and Alex Eagle for
their help as I delved into &lt;code&gt;bzlmod&lt;/code&gt;. Alex&amp;rsquo;s &lt;a href=&#34;https://blog.aspect.dev/bzlmod&#34;&gt;blog post&lt;/a&gt;
gave me the incentive to start digging into &lt;code&gt;bzlmod&lt;/code&gt; and provided
enough scaffolding for me to get started. It proved invaluable!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Gravity</title>
      <link>https://www.rocketpoweredjetpants.com/2021/11/gravity/</link>
      <pubDate>Tue, 30 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2021/11/gravity/</guid>
      <description>&lt;p&gt;Our &lt;a href=&#34;https://solarsystem.nasa.gov/solar-system/our-solar-system/in-depth/#otp_formation&#34;&gt;solar system formed&lt;/a&gt; from a cloud of dust and
gas. Sometimes, gravity pulled together spectacularly large quantities
of matter together, and we ended up with the Sun and the gas
giants. Sometimes, the pull of gravity ended up forming something far
smaller, like Pluto or Mercury. The asteroid belt? It&amp;rsquo;s the place
where gravity wasn&amp;rsquo;t quite enough to coalesce matter into Yet Another
Planet.&lt;/p&gt;
&lt;p&gt;Gravity: it&amp;rsquo;s a big deal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our code has gravity&lt;/strong&gt;. Each repository begins with a single line of
code in a single file. Code accumulates. We write more to extend the
functionality of our programs. And then, we find that we need to share
some code between two repositories, and we have a choice.&lt;/p&gt;
&lt;p&gt;We go one way, and each repository pretends it&amp;rsquo;s independent of the
other, pushing and pulling that shared code to a central place such as
a package manager. But they still perturb each other&amp;rsquo;s orbit. A
backward-incompatible change in one repo causes the other to flex and
change too. A feature request from one leads the other to change. They
are independent, but tied, inextricably, by an invisible force.&lt;/p&gt;
&lt;p&gt;We go the other way, and colocate code until we have a giant
repository. A monorepo, or one of several überrepos. In a larger repo
that invisible force is far stronger, because the code and the thing
that depend on it are far closer. The same backward incompatible
change that took a long time to spot when the repos were separate
becomes visible instantly.&lt;/p&gt;
&lt;p&gt;You see? &lt;strong&gt;Code has gravity&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Just like our solar system, that gravity leads to different
outcomes. Sometimes, the gravitational pull is sufficient to pull
almost &lt;a href=&#34;https://research.google/pubs/pub45424/&#34;&gt;everything into a single place&lt;/a&gt;. Sometimes, there are
other forces at play that lead to the &lt;a href=&#34;https://www.youtube.com/watch?v=kb-m2fasdDY&#34;&gt;exact opposite
outcome&lt;/a&gt;. Frequently, I&amp;rsquo;ve seen repositories start as
independent bodies, and then, as their sizes increase and their
interactions multiply, they come every closer together, sometimes
merging. I&amp;rsquo;ve seen the opposite happen too. Repos figuring out ways of
overcoming the gravity between the components and breaking out of the
larger body to stand alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our code has gravity&lt;/strong&gt;. Our repos are caught in an endless dance,
drawing closer, fragmenting, pulling apart, and then coming back
together again.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cloud Workstation</title>
      <link>https://www.rocketpoweredjetpants.com/2021/03/cloud-workstation/</link>
      <pubDate>Wed, 17 Mar 2021 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2021/03/cloud-workstation/</guid>
      <description>&lt;p&gt;Sometimes, just sometimes, I need to work on a machine that isn&amp;rsquo;t
running macOS. Previously, I&amp;rsquo;ve done this by installing either a local
Docker image, or by running a VM. Neither of these approaches was
terribly satisfactory, and both became less viable when I switched to
an M1 MacBook Air (which, let it be known, is an absolutely lovely
piece of kit) Today, I explored a new approach: using a cloud-based
workstation.&lt;/p&gt;
&lt;p&gt;The downside of this approach is that the workstation costs actual
money to use, but the advantage is that I now have a machine that I
can use whenever I need it, from wherever I happen to be. Because I&amp;rsquo;m
only using this occasionally, as long as I remember to shutdown the
image, the costs are pretty reasonable.&lt;/p&gt;
&lt;p&gt;Setting up my first one was ridiculously easy using
&lt;a href=&#34;https://cloud.google.com/compute&#34;&gt;GCP&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Since I&amp;rsquo;m not the sort of person who&amp;rsquo;s great at reading docs, I just
followed the steps from a &lt;a href=&#34;https://www.youtube.com/watch?v=L31yQYyMkKs&#34;&gt;YouTube
video&lt;/a&gt;. Summarised, this
is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new VM instance in GCP. I picked an instance with 8 CPU
cores and 32GB of RAM. That may be a little much, but the CPU is
important for me, since I want my compile and test runs to be as
fast as possible.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the OS to Ubuntu, and choose which version you want to use.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure you pick the right kind of disk. For development work,
using an SSD is a really good idea, and making sure that there&amp;rsquo;s
enough disk space is vital.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once that&amp;rsquo;s done, you can start up the VM and use &lt;code&gt;gcloud&lt;/code&gt; to SSH on
to it. Once you&amp;rsquo;ve done that, just go ahead and install everything you
need to get your development done. We can use pinned browsers running
headless in the &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/&#34;&gt;Selenium&lt;/a&gt;
build, so there&amp;rsquo;s less need for an X Windows environment, but setting
one of those up seems easy enough.&lt;/p&gt;
&lt;p&gt;Of course, I had no idea how much disk I actually needed, and had
seleted a &amp;ldquo;balanced&amp;rdquo; disk that was waaaay too small. Fixing that was
pretty simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop the VM.&lt;/li&gt;
&lt;li&gt;Create a snapshot of the disk.&lt;/li&gt;
&lt;li&gt;Create a new disk from the snapshot, choosing both SSD and a
reasonable amount of disk space.&lt;/li&gt;
&lt;li&gt;Edit the VM instance that you&amp;rsquo;re using as a workstation, and scroll
down to &amp;ldquo;boot disk&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;Nuke the existing disk, and then select to use the newly created
disk image using &lt;code&gt;Add Item&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After this, restart the VM, give it a chance to reboot, and then
you&amp;rsquo;re all good to go!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Migration Complete?</title>
      <link>https://www.rocketpoweredjetpants.com/2020/08/migration-complete/</link>
      <pubDate>Mon, 31 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2020/08/migration-complete/</guid>
      <description>&lt;p&gt;If everything has gone according to plan, my blog should now be hosted properly on the new site. Fingers crossed, eh?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Regular and Infrequent Performance Reviews</title>
      <link>https://www.rocketpoweredjetpants.com/2020/08/regular-and-infrequent-performance-reviews/</link>
      <pubDate>Sat, 22 Aug 2020 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2020/08/regular-and-infrequent-performance-reviews/</guid>
      <description>&lt;p&gt;Ah! Performance reviews! Love &amp;rsquo;em or loathe &amp;rsquo;em, they&amp;rsquo;re a feature of corporate life. I&amp;rsquo;ve only worked in places that do performance reviews every six-to-twelve months. Maybe you work somewhere where performance reviews are done differently. If that&amp;rsquo;s true, I&amp;rsquo;m happy for you, and this post won&amp;rsquo;t be useful for you.&lt;/p&gt;
&lt;p&gt;If that&amp;rsquo;s not true, and you&amp;rsquo;re in a place that does these &amp;ldquo;regular but infrequent&amp;rdquo; perf reviews, let me explain why I hate them with a passion, and then suggest some changes that might make them more tolerable and fit for purpose.&lt;/p&gt;
&lt;p&gt;First of all, why do we do performance reviews? There are two main reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To help us figure out what our areas of strength are, and which areas we should be focusing on improving or to avoid doing.&lt;/li&gt;
&lt;li&gt;To help our manager (and reporting chain) identify the same.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That is, performance reviews aren&amp;rsquo;t just a feel-good (feel-bad?) exercise. Not only do they give us a chance to pause and reflect about what we&amp;rsquo;re doing in a way that we seldom do, but they also serve a purpose for the company that we&amp;rsquo;re part of. Frequently, performance reviews feed into things like promotions and whether or not to put someone on a &amp;ldquo;performance improvement plan&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The problem is (IMHO) performance reviews run at &amp;ldquo;regular but infrequent&amp;rdquo; intervals are incredibly unhelpful. How come?&lt;/p&gt;
&lt;p&gt;If the only time I&amp;rsquo;m encouraged to really stop and think how I&amp;rsquo;m doing in my job is once every six months (or, worse, twelve months) then something has gone horribly awry. The feedback we get as part of a performance allows for &amp;ldquo;course correction&amp;rdquo;. Our 1:1s with our managers should be providing some of this, but getting feedback from peers is absolutely essential. Without that feedback, we can go for an excessive length of time accidentally making things worse.&lt;/p&gt;
&lt;p&gt;So, the first reason I hate &amp;ldquo;regular and infrequent&amp;rdquo; perf reviews? They&amp;rsquo;re too little, too late.&lt;/p&gt;
&lt;p&gt;Honestly, I could stop here. That&amp;rsquo;s the biggest reason I hate them. I could stop, but I&amp;rsquo;m not going to.&lt;/p&gt;
&lt;p&gt;What would I prefer? Some places offer &amp;ldquo;continuous&amp;rdquo; feedback (&lt;a href=&#34;https://blog.impraise.com/360-feedback/a-new-era-for-goldman-sachs-and-performance-appraisals-performance-review&#34;&gt;Goldman Sachs for example&lt;/a&gt;. Goldman Sachs!) That is, when there&amp;rsquo;s a suitable point for feedback to be collected and given, it&amp;rsquo;s collected and given. That may be at the end of a project, at particular milestones, or at some finer-grain than &amp;ldquo;every six months&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Another problem with &amp;ldquo;regular and infrequent&amp;rdquo; feedback loops is that it doesn&amp;rsquo;t foster a culture of people giving helpful feedback at the point where it could be most effective. Instead, people end up feeling ambushed by feedback that people have been harbouring, holding, and (possibly) festering on for up to a year. The best feedback I ever got was mid-way through helping to organise a conference, where one of the other people helping took me aside and told me exactly how they perceived my work, and what I could do to improve. It wasn&amp;rsquo;t comfortable to hear, and it can&amp;rsquo;t have been comfortable to give, but I listened and changed, and that helped everyone around me, and helped us smooth the work of getting that conference sorted out.&lt;/p&gt;
&lt;p&gt;What would I prefer? At the very least, having feedback after milestone events in a project, and ideally when I (or my manager) think it would be helpful. Even better would be to be in a culture where people felt able to provide feedback as they deemed it necessary and when it would be helpful. It&amp;rsquo;s probably not a revelation that timely, helpful feedback is preferred to untimely, ambush-style feedback, so it puzzles me why I&amp;rsquo;ve seen so much of the latter and so little of the former.&lt;/p&gt;
&lt;p&gt;Giving and receiving feedback can be very difficult. Pat Kua has a series of blog posts on this subject, but I find &lt;a href=&#34;https://www.thekua.com/atwork/2017/03/the-gift-of-feedback-in-a-booklet/&#34;&gt;this post&lt;/a&gt; a good jumping off point.&lt;/p&gt;
&lt;p&gt;However! Perf reviews aren&amp;rsquo;t just for the individual! They&amp;rsquo;re for the company too, and &amp;ldquo;regular but infrequent&amp;rdquo; perf reviews are a disaster for them.&lt;/p&gt;
&lt;p&gt;Consider the way that feedback is normally gathered. Junior engineers typically ask people in their own team, and their tech leads. Senior engineers, ask folks on other teams they interact with, frequently the other senior engineers. Managers seeking to support people looking for promotions tend to ask tech leads of teams and senior engineers for more detailed feedback too.&lt;/p&gt;
&lt;p&gt;What does this mean?&lt;/p&gt;
&lt;p&gt;It means that during &amp;ldquo;perf review season&amp;rdquo; a company&amp;rsquo;s most senior and influential engineers are no longer writing code and guiding teams. Instead, they&amp;rsquo;re writing walls of feedback for people, at best only being able to focus part of their attention on the projects they&amp;rsquo;re working on. The junior engineers tend to have less feedback to write, and get back to the grindstone sooner. This is clearly deleterious to the quality and progress of the projects.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s compounded by the fact that management is also soaked up in a massive effort to collect, collate, and standardise the feedback that&amp;rsquo;s coming in. Frankly, it leads to a huge uptick in stress and chaos, neatly targeted at a company&amp;rsquo;s leadership.&lt;/p&gt;
&lt;p&gt;That can&amp;rsquo;t be good.&lt;/p&gt;
&lt;p&gt;What would be better? Spreading the load over the course of the year would help. Doing one or two pieces of feedback every week is far less of a chore for the people providing that feedback. People who have just reached a milestone and have some space to breathe, with lessons still fresh in their minds, provide more useful feedback.&lt;/p&gt;
&lt;p&gt;The downside with continuous feedback like this is that more rigour needs to be put in place to ensure that people are judged fairly and consistently. I don&amp;rsquo;t know the best way of doing that (sampled feedback, collected and reviewed using the current techniques? A subset of people asked for feedback using the &amp;ldquo;regular and infrequent&amp;rdquo; process?), but I bet you someone smarter than me has been thinking about it already.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rebuilding the Site</title>
      <link>https://www.rocketpoweredjetpants.com/2020/07/rebuilding-the-site/</link>
      <pubDate>Mon, 27 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2020/07/rebuilding-the-site/</guid>
      <description>&lt;p&gt;The last time I updated this site was sometime in 2012. I put up a placeholder
page, meaning to do far more, started using Blogger for my blog rather than a
home-grown solution (again, meaning to find a way to import the old content),
and then… and then… and then life happened, and I never seemed to do anything
about it.&lt;/p&gt;
&lt;p&gt;Then came the pandemic, and eventually, encouraged by &lt;a href=&#34;https://www.pinarmavi.com&#34;&gt;my
wife&lt;/a&gt;, I decided to refresh the whole thing.&lt;/p&gt;
&lt;p&gt;The old site had been hosted on &lt;a href=&#34;https://cloud.google.com/appengine/docs&#34;&gt;Google App
Engine&lt;/a&gt; and I was pretty happy with
it: SSL was zero-hassle, setup was simple, and it seemed like a safe bet. Eight
years later, and App Engine isn&amp;rsquo;t the shinest, spiffiest thing on the block
right now, but it has shown itself to be reliable and unfussy. I like reliable
and unfussy in my tech. I like
&lt;a href=&#34;https://mcfunley.com/choose-boring-technology&#34;&gt;boring&lt;/a&gt;. So it seemed like a
good idea to continue using GAE.&lt;/p&gt;
&lt;p&gt;However, I wanted to move off Blogger, and I wanted the site design to be a
little more interesting, to work on mobile, and to be as fast and light as
possible. So I had a look around, and chose the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google App Engine&lt;/li&gt;
&lt;li&gt;Hugo with the Minimal theme&lt;/li&gt;
&lt;li&gt;A private repo on GitHub&lt;/li&gt;
&lt;li&gt;Deployed using GitHub actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point is particularly nice: I just need to push, and the site gets
updated automatically at some not-too-distant point in the future. So, how did
I do it?&lt;/p&gt;
&lt;h2 id=&#34;hugo&#34;&gt;Hugo&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://gohugo.io&#34;&gt;Hugo&lt;/a&gt; is pretty easy to set up. The docs are excellent, and
after browsing
through the &lt;a href=&#34;https://themes.gohugo.io&#34;&gt;Hugo Themes&lt;/a&gt; site, it looked like the
&amp;ldquo;&lt;a href=&#34;https://github.com/calintat/minimal&#34;&gt;Minimal&lt;/a&gt;&amp;rdquo; theme hit many of my
requirements and was also pretty light. Getting the skeleton set up was an
absolute breeze.&lt;/p&gt;
&lt;p&gt;Rather than using a &lt;code&gt;git&lt;/code&gt; submodule, I used
&lt;a href=&#34;https://docs.github.com/en/github/importing-your-projects-to-github/support-for-subversion-clients&#34;&gt;GitHub&amp;rsquo;s SubVersion support&lt;/a&gt;
and grabbed the theme using &lt;code&gt;svn export&lt;/code&gt;: I knew that I&amp;rsquo;d be making tweaks, and I
really do want to understand CSS and modern HTML better, so being able to do an
update wasn&amp;rsquo;t so important. This also meant that I avoided the full horror of
git submodules, which have always seemed an inelegant way of expressing
modularity to me.&lt;/p&gt;
&lt;h2 id=&#34;migrating-from-blogger&#34;&gt;Migrating from Blogger&lt;/h2&gt;
&lt;p&gt;I had thought that this would be a nightmare. I wanted the old URLs to continue
to work, and it was this that had stopped me dead in my tracks. I need not have
worried. A little hunting found
&lt;a href=&#34;https://github.com/palaniraja/blog2md&#34;&gt;blog2md&lt;/a&gt;, which is a useful tool to
take a Blogger (or, it turns out, Wordpress) backup, and convert that into
MarkDown with front-matter that Hugo can consume.&lt;/p&gt;
&lt;p&gt;My blog on Blogger was pretty minimal, and there were no images, so running
&lt;code&gt;node index.js b blogger-exoport.xml out&lt;/code&gt; did what I wanted. The only problem
were the comments, but since I&amp;rsquo;d done a pretty poor job keeping spammers out of
them, I felt it okay (though a little sad) to delete them.&lt;/p&gt;
&lt;p&gt;The hardest bit was navigating the Blogger admin console to find out how to do
the backups. If you&amp;rsquo;re following in my footsteps, that&amp;rsquo;s currently hidden under
&lt;code&gt;Settings -&amp;gt; Manage blog -&amp;gt; Back up content&lt;/code&gt;. It&amp;rsquo;s quite a long way down the
settings, and I managed to overlook it at first. D&amp;rsquo;oh!&lt;/p&gt;
&lt;h2 id=&#34;hosting-on-app-engine&#34;&gt;Hosting on App Engine&lt;/h2&gt;
&lt;p&gt;The nice thing with a static site is that it&amp;rsquo;s really simple to host. Anywhere
with a working web server will do. However, I&amp;rsquo;ve used App Engine for a while
now and I quite like it, so I don&amp;rsquo;t see a need to jump on to the Fastly of
Netlify trains yet. Besides, I&amp;rsquo;ve already spent part of my &amp;ldquo;interesting&amp;rdquo;
budget on Hugo (which we also use for the &lt;a href=&#34;https://selenium.dev&#34;&gt;Selenium&lt;/a&gt;
website), and I want to spend the rest on the deployment pipeline. App Engine
it is, then.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;app.yaml&lt;/code&gt; is remarkably simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;runtime&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;python27&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;api_version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;threadsafe&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;handlers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;static_files&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/index.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;upload&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;secure&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Standard index.html check&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/(.*)/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;static_files&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/\1/index.html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;upload&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;secure&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/(.*\..*)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;static_files&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/\1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;upload&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;public/(.*)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;secure&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most of this is deeply unexciting (yay!) We&amp;rsquo;re using a lightweight runtime
(python), but we could have used anything. We force all the handlers to use TLS
where possible, and we check for an &lt;code&gt;index.html&lt;/code&gt; file every time you visit a
directory (which has been standard practice since the dawn of the Web)&lt;/p&gt;
&lt;p&gt;One thing to note is that the content is all served from &lt;code&gt;public&lt;/code&gt;. That&amp;rsquo;s the
directory generated by Hugo when it&amp;rsquo;s run.&lt;/p&gt;
&lt;p&gt;Having set up the basic app, it was possible to deploy from the command line,
and everything looked pretty good, except I needed to configure TLS in order to
have everything work on a custom domain. To do this, I needed to prove that I
was the owner of the domain, and then the App Engine console has a handy option
in &lt;code&gt;Settings -&amp;gt; Custom Domains&lt;/code&gt; to &lt;code&gt;Enable Managed Security&lt;/code&gt;. This will take
care of creating and renewing SSL certs for you, so it becomes a no-brainer to
just flip the switch.&lt;/p&gt;
&lt;h2 id=&#34;deploying-via-github-actions&#34;&gt;Deploying via GitHub Actions&lt;/h2&gt;
&lt;p&gt;The final piece of the puzzle was to use GitHub Actions to deploy the site when
I push to the repo hosting the content. I didn&amp;rsquo;t want to have to check in
generated content (because &amp;ldquo;whhhhyyyy!?&amp;rdquo;). In the end I ended up with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;CI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;branches&lt;/span&gt;: [ &lt;span style=&#34;color:#ae81ff&#34;&gt;trunk ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;workflow_dispatch&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;branches&lt;/span&gt;: [ &lt;span style=&#34;color:#ae81ff&#34;&gt;trunk ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# The type of runner that the job will run on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Setup Python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-python@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;python-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3.8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Hugo setup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;peaceiris/actions-hugo@v2.4.12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;hugo-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.71.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Build site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hugo &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Login&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;GoogleCloudPlatform/github-actions/setup-gcloud@master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;290.0.1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;project_id&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mysite&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;service_account_key&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.GCP_SA_KEY }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;export_default_credentials&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gcloud info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;GoogleCloudPlatform/github-actions/appengine-deploy@master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;project_id&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mysite&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The hardest bit was figuring out that I needed to install Python into the
pipeline, or the Google tools would all get very grumpy. That, and remembering
to
&lt;a href=&#34;https://cloud.google.com/appengine/docs/standard/python/access-control#service_account&#34;&gt;set up a service account&lt;/a&gt;
to do the deployments for me, remembering to store the secrets
in &lt;a href=&#34;https://docs.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets&#34;&gt;GitHub Secrets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Actually, I lied. The hardest bit was figuring out what IAM roles were needed
for the service account to use. I&amp;rsquo;ve ended up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App Engine Deployer&lt;/li&gt;
&lt;li&gt;App Engine Service Admin&lt;/li&gt;
&lt;li&gt;Cloud Build Service Account&lt;/li&gt;
&lt;li&gt;Storage Object Creator&lt;/li&gt;
&lt;li&gt;Storage Object Viewer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After creating those and &lt;a href=&#34;https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/&#34;&gt;manual reruns&lt;/a&gt;
(which explains the &lt;code&gt;workflow_dispatch&lt;/code&gt; section at the top) I finally got
everything up and running.&lt;/p&gt;
&lt;p&gt;In the end, the actual effort was a pleasant evening&amp;rsquo;s worth of mucking around
with Hugo, App Engine (it&amp;rsquo;s been a while), and GitHub Actions. I&amp;rsquo;m pretty happy
with the result :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Writing Again</title>
      <link>https://www.rocketpoweredjetpants.com/2020/07/writing-again/</link>
      <pubDate>Mon, 27 Jul 2020 00:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2020/07/writing-again/</guid>
      <description>&lt;p&gt;A friend of mine messaged me a while ago, and reminded me that my blog had been
dormant for a very long time. They were right. I&amp;rsquo;ve not sat and written down my
thoughts for a long time. It&amp;rsquo;s time to get started again.&lt;/p&gt;
&lt;p&gt;Now, what to write about….&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Many Months in Selenium: to November</title>
      <link>https://www.rocketpoweredjetpants.com/2018/11/many-months-in-selenium-to-november/</link>
      <pubDate>Mon, 26 Nov 2018 16:36:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/11/many-months-in-selenium-to-november/</guid>
      <description>&lt;p&gt;Well, it&amp;rsquo;s been a long time since I sat down and wrote a post about the adventures in Selenium-land. Time for an update!&lt;/p&gt;
&lt;p&gt;Since I last wrote, the work of updating the JSON handling code in the java tree has been completed, and it appears to be stable. However, it would be a terrible waste of time if that was all that we had done, and fortunately it&amp;rsquo;s not :)&lt;/p&gt;
&lt;p&gt;The big thing is that we&amp;rsquo;ve now finished the 3.x release cycle, and we&amp;rsquo;re getting ready for 4.0. Someone foolishly let me pick version numbers, so the last few releases of 3 tended to get ever closer to π. Judging by some of the bug reports, the initial jump, from 3.14 to 3.141 appears to have confused some folk, but now that we&amp;rsquo;ve reached 3.141.59 I think the point has been made (and, just maybe, the joke is wearing thin)&lt;/p&gt;
&lt;p&gt;The 4.0 release is going to be a lot of fun. My main focus has been the new &lt;a href=&#34;https://docs.google.com/document/d/147lfD88VYrgSdg4X3agFpnpcrHBlVUMI3sO6SGVUxF8/edit?usp=sharing&#34;&gt;Selenium Grid&lt;/a&gt;, which features a more modern design, for use with things such as AWS and Kubernetes (and docker compose). Of course, maintaining a simple user experience has been high on our list of goals, so people used to the existing approach of &amp;ldquo;hub&amp;rdquo; and &amp;ldquo;node&amp;rdquo; will continue to be able to run the system like that. The biggest change is that under-the-covers, the new standalone server and the new Grid are exactly the same software, which is a huge change from the current approach where we have two not-terribly-well-integrated codebases in the same binary.&lt;/p&gt;
&lt;p&gt;Another big change is that we&amp;rsquo;re exploring the move from using &lt;a href=&#34;https://buckbuild.com/&#34;&gt;Buck&lt;/a&gt; to build much of Selenium to &lt;a href=&#34;https://bazel.build/&#34;&gt;Bazel&lt;/a&gt;. This hasn&amp;rsquo;t been something I&amp;rsquo;ve been keen on doing, since I used to be the tech lead on Buck, and I think it has a number of useful properties. Despite this, Bazel has a comparatively huge amount of community support, and that means that people wanting to hack on the project have a smaller learning curve to climb.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Month in Selenium: March</title>
      <link>https://www.rocketpoweredjetpants.com/2018/03/a-month-in-selenium-march/</link>
      <pubDate>Sat, 31 Mar 2018 17:56:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/03/a-month-in-selenium-march/</guid>
      <description>&lt;p&gt;The month from February to March has been a fun one. At the beginning of March, I attended SauceCon, and gave a keynote on &amp;ldquo;&lt;a href=&#34;https://www.youtube.com/watch?v=uHnJsh7Wyok&amp;amp;t=1837s&#34;&gt;Lessons From a Decade in Selenium&lt;/a&gt;&amp;rdquo;. While the original talk had focused on milestones such as when we first started shipping code, or when we switched to Git, or when someone joined the project, as I sat in the airport waiting to fly, I realised that this was an incredibly dull talk; surely the point of keynote is to give people something to think about and consider?&lt;/p&gt;
&lt;p&gt;This explains why I was busy rewriting the entire thing at 12km above the ground in a metal tube zipping along at a smidge over 900km/h.&lt;/p&gt;
&lt;p&gt;In the end, I spoke about what makes working on Selenium so rewarding, focusing on the themes of &amp;ldquo;Joy&amp;rdquo;, &amp;ldquo;Serendipity&amp;rdquo;, &amp;ldquo;Thankfulness&amp;rdquo;, &amp;ldquo;Community&amp;rdquo;, &amp;ldquo;Growth&amp;rdquo;, and &amp;ldquo;Striving&amp;rdquo;. I&amp;rsquo;ve yet to see the official feedback, but I believe that the talk was well received, as people kept returning to the main themes throughout the conference.&lt;/p&gt;
&lt;p&gt;SauceCon itself was a lot of fun. We were lucky to have some of the Selenium committers (old and new), and also supporters of the project from companies such as Sauce Labs itself, and Applitools (who are providing almost all the effort going into the new Selenium IDE) In addition, the Appium developers were well represented too. It was great to be surrounded by so many people who have spent so much time pouring energy into Open Source Software, and to catch up with some of my favourite people. There&amp;rsquo;s a &lt;a href=&#34;https://twitter.com/jlipps/status/973230156541198343&#34;&gt;lovely photo&lt;/a&gt; of Jonathan Lipps and myself in matching bowling shirts, which I&amp;rsquo;m happy to see he tweeted.&lt;/p&gt;
&lt;p&gt;Since we had so many people in the same place, we decided to release Selenium 3.10. The main highlights of this release were behind the scenes for most users, as we focused on the continued clean up of the internal of Grid, and the continued use of our own abstractions to handle HTTP and JSON. Having said this, there were user-supplied patches, notably moving us from Selenium&amp;rsquo;s own &amp;ldquo;Duration&amp;rdquo; class to the one that ships with the JRE. Deleting code is a lot of fun.&lt;/p&gt;
&lt;p&gt;One reason for shipping 3.10 was to lay the groundwork for a terrible dad-joke: releasing Selenium 3.11 on the 11th March (3/11 in US date format). Jim Evans and I had noticed that 3.11 was also one of the most famous of the Windows releases, so we decided to lean into the joke, and shipped &amp;ldquo;Selenium for Workgroups&amp;rdquo; as well in March. The Selenium server even &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/commit/e5e7e9000be0cfea807d79af9bccf23e11a3514b&#34;&gt;reports this to users&lt;/a&gt;. 3.12 won&amp;rsquo;t have this feature.&lt;/p&gt;
&lt;p&gt;In a bid to help our Windows developers ship the Selenium jars, I merged a ton of upstream changes to our fork of Buck, and then spent some time attempting to resolve the issue where zips created on Windows create unreadable directories when unpacked. My fix doesn&amp;rsquo;t resolve the issue, so I &lt;a href=&#34;https://github.com/facebook/buck/issues/1790&#34;&gt;filed an issue&lt;/a&gt; with the upstream Buck project in the hope that they&amp;rsquo;d fix it for me. If I get some free time, I&amp;rsquo;ll try this as well.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also been working on replacing GSON within our tree (though on my local machine). By the end of the month of work, I had a forked version of Selenium that didn&amp;rsquo;t use GSON at all for outputting JSON. Sadly, I was a little over-ambitious when attempting to finish the work by also deserialising from JSON to proper types. It turns out that there&amp;rsquo;s a bunch of code in Grid that relies on the current semantics of GSON to function. Stepping back, it looks like most of this is because GSON isn&amp;rsquo;t aware of our own types, and it should be relatively easy to replace some of this. At least I know I should be working on next….&lt;/p&gt;
&lt;p&gt;Well, that, and a new way of starting sessions that allow users to properly make use of all the features that the W3C New Session command offer.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Month in Selenium: February</title>
      <link>https://www.rocketpoweredjetpants.com/2018/03/a-month-in-selenium-february/</link>
      <pubDate>Tue, 13 Mar 2018 18:53:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/03/a-month-in-selenium-february/</guid>
      <description>&lt;p&gt;January was a quiet month for Selenium hacking, but it laid the groundwork for February&amp;rsquo;s efforts. These largely centred around code cleanup in the Grid server, and migrating the project to make better use of our own abstractions over &lt;a href=&#34;http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/json/package-summary.html&#34;&gt;JSON&lt;/a&gt; and &lt;a href=&#34;http://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/remote/http/HttpClient.html&#34;&gt;HTTP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Why do we have our own abstractions for these incredibly common tasks? There are two main reasons. The first is that we&amp;rsquo;d like freedom to be able to choose our underlying implementation for these things, without needing to extensively rework our own first-party code. The second is that third party libraries offer generalised APIs that need to meet the needs of all users, whereas we have very specific needs met by these APIs and may need to work around some of the sharp edges (for example, in the java code, lots of classes that need to be serialised to JSON have a toJson method that GSON knows absolutely nothing about). This is typically done by writing &lt;a href=&#34;https://en.wikipedia.org/wiki/Adapter_pattern&#34;&gt;adapters&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We started using the &lt;a href=&#34;https://hc.apache.org/httpcomponents-client-4.5.x/index.html&#34;&gt;Apache HttpClient&lt;/a&gt; by default as it&amp;rsquo;s the HTTP library used by &lt;a href=&#34;http://htmlunit.sourceforge.net/&#34;&gt;HtmlUnit&lt;/a&gt;, which we used to ship as part of the core Selenium distribution. In keeping with the other drivers out there, the HtmlUnit team now work on the HtmlUnitDriver, so it&amp;rsquo;s no longer kept in the main project source repo. The interesting thing is that since we made the choice a long, long time ago to use the HttpClient, the HTTP standard has moved forward. HTTP/2 is now a thing. HTTP/2 support is coming as part of HttpClient 5. In order to take advantage of the new options and capabilities, we&amp;rsquo;d have to rework our existing abstractions anyway, so why not take a look around for something else to use? Better yet, if we use an HTTP library that isn&amp;rsquo;t a dependency of one of &lt;em&gt;our&lt;/em&gt; dependencies, we&amp;rsquo;re less likely to end up with clashing versions.&lt;/p&gt;
&lt;p&gt;One of the reasons that Java has a terrible reputation for start up speed is because people have massively bloated classpaths. As it stands, the Selenium standalone server weighs in at a portly 24MB. The Apache HttpClient weighs in at about 1.4MB of this total, before we do the update. After the update, the beta of 5 is a touch under 1MB. In comparison, &lt;a href=&#34;http://square.github.io/okhttp/&#34;&gt;OkHttp&lt;/a&gt; (which already supports HTTP/2) with its dependencies is approximately 500kb. In other words, OkHttp is smaller, already supports HTTP/2, and isn&amp;rsquo;t a dependency of our dependency.&lt;/p&gt;
&lt;p&gt;So, we switched the project to use OkHttp instead of the Apache HttpClient.&lt;/p&gt;
&lt;p&gt;Within the client code, making this change was relatively trivial. The problem is that the server-side code had leaked Apache&amp;rsquo;s APIs into the code. Before we can replace the Apache HttpClient, we need to first of all replace all those usages. That&amp;rsquo;s made somewhat harder by the fact that it&amp;rsquo;s exposed as part of the public APIs of various classes that other libraries extend.&lt;/p&gt;
&lt;p&gt;Fortunately, we have a process for deprecating and deleting APIs. First of all, we mark the methods to be deleted as &amp;ldquo;deprecated&amp;rdquo; for at least one release. And then we delete them. Of course, if you&amp;rsquo;re going to deprecate a method, you really should provide an alternative and migrate as many uses as can be found to use the replacements. A bulk of my work this month was spent making these changes.&lt;/p&gt;
&lt;p&gt;Of course, we needed to do a release, so we lined up 3.9 to start the process. In order to do the release, we needed to actually build it. There had been reports of some issues building the release artefacts on Windows. To resolve this, I had to update our fork of &lt;a href=&#34;https://github.com/seleniumhq/buck&#34;&gt;Buck&lt;/a&gt; to pull in the latest changes from Facebook, and then to try and work around those issues. Naturally, the Buck developers aren&amp;rsquo;t aware of our fork, so merging in their changes was a somewhat time-consuming affair. Once that I was done, I wrote what I thought was a fix and pushed a new version of our fork of Buck.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t work. Oh well.&lt;/p&gt;
&lt;p&gt;The final step in doing a release is trying to get our &lt;a href=&#34;https://travis-ci.org/SeleniumHQ/selenium&#34;&gt;CI builds&lt;/a&gt; green. These take an incredible amount of time to run, and I wondered whether we could speed them up. Travis has &lt;a href=&#34;https://docs.travis-ci.com/user/caching&#34;&gt;support for caching&lt;/a&gt;, so it would be nice to use that. My attempts to use caching were foiled because the cache takes into account environment variables, which we use to separate our builds. There&amp;rsquo;s a &lt;a href=&#34;https://github.com/travis-ci/travis-ci/issues/5898&#34;&gt;bug open in the Travis tracker&lt;/a&gt; to allow us to name builds, which would have allowed us to work around this, but it&amp;rsquo;s still open. Ho hum. As a work around, I wrote a simple &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/commit/ecdad5f4209607c285edd497a556e5ebbc1ed599#diff-07d4b4425148909b7db329e3bb0c95d5&#34;&gt;wrapper&lt;/a&gt; around Buck that we can call within our CI servers. This makes better use of Buck&amp;rsquo;s ability to parallelise work automatically, and this has helped bring our build times down. Hurrah!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Two Months in Selenium - November and December</title>
      <link>https://www.rocketpoweredjetpants.com/2018/01/two-months-in-selenium-november-and-december/</link>
      <pubDate>Sun, 21 Jan 2018 18:19:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/01/two-months-in-selenium-november-and-december/</guid>
      <description>&lt;p&gt;You may have noticed a distinct lack of an update last month. It&amp;rsquo;s because I was focused on client work, Christmas, and the New Year, and took some time away from the keyboard. But I&amp;rsquo;m back now!&lt;/p&gt;
&lt;p&gt;The W3C WebDriver spec is now at the stage where we need to demonstrate multiple compatible implementations. Realistically, this means that we need two passes for each test in our &lt;a href=&#34;https://w3c.github.io/webdriver/results/html/all.html&#34;&gt;test suite&lt;/a&gt;. The browser vendors are working hard to get things working, and progress is being made. There&amp;rsquo;s not been a huge amount for me to do here, so this is more of a waiting game than anything else from my perspective. Having said that, I&amp;rsquo;m on the hook for some sections in Level 2, so I should really sit down and write those (and the matching tests)&lt;/p&gt;
&lt;p&gt;The main thing I&amp;rsquo;ve been focused on has been the Selenium Grid. There are a couple of things that we really need to solve with Grid. The first is that the code is complex and hard to deal with. When we originally released it, it took a huge amount of work to review the code for thread-safety and to debug many of the issues. That code has not become easier to reason about, which makes it harder to foster Open Source contributions.&lt;/p&gt;
&lt;p&gt;Of course, that&amp;rsquo;d be fine if we didn&amp;rsquo;t care about making any changes, but we do. When Grid came into being, it was normal to have a physical server for each node in the grid. If you were lucky, you might have a massive server with VMWare running on it, which you&amp;rsquo;d cycle virtual machines on to keep the Grid healthy. The world has changed. &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt; is now A Thing, and there are multiple &amp;ldquo;Selenium as a Service&amp;rdquo; (SaaS) cloud providers.&lt;/p&gt;
&lt;p&gt;There are some projects out there that implement some of the functionality of Grid. For example, &lt;a href=&#34;http://aerokube.com/selenoid/latest/&#34;&gt;selenoid&lt;/a&gt; makes use of Docker, but it doesn&amp;rsquo;t use the W3C dialect of the webdriver protocol, which means it doesn&amp;rsquo;t do protocol conversion, and it doesn&amp;rsquo;t natively support cloud providers. &lt;a href=&#34;https://zalando.github.io/zalenium/&#34;&gt;Zalenium&lt;/a&gt; builds on top of Grid, and provides support for Docker and SaaS, but they&amp;rsquo;ve had to work within the existing architecture, and there are obvious rough edges.&lt;/p&gt;
&lt;p&gt;Finally, we&amp;rsquo;ve wanted the selenium server to be a &amp;ldquo;Grid of one&amp;rdquo;. If you go into the code of the server, you&amp;rsquo;ll see that there are two fairly separate trees that live side-by-side. When you start the server, it picks one and then goes with it. It&amp;rsquo;d made things like supporting the W3C protocol harder than it should be, and it&amp;rsquo;s not an elegant way to run things.&lt;/p&gt;
&lt;p&gt;As a solution to this, it seems obvious that there should only be one code path. The problem is that the standalone server is too simplistic about how it assigns work, and (as discussed) the Grid code is too complex. Over the past few releases, I&amp;rsquo;ve been landing code to help resolve this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pass through mode: this makes the server proxy requests without doing an parsing or changes unless necessary.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/ActiveSession.java&#34;&gt;ActiveSession&lt;/a&gt; abstraction has been added. This makes adding new types of provider (SaaS, Docker) far easier to write.&lt;/li&gt;
&lt;li&gt;A &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/NewSessionPipeline.java&#34;&gt;new session pipeline&lt;/a&gt;&amp;rdquo; has been added, and this is being used to handle things like multiple versions of the webdriver protocol.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most recent thing I&amp;rsquo;ve been working on has been a new scheduler. This will be rolled into the new session pipeline, and is composed of a number of pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/scheduler/Scheduler.java&#34;&gt;Scheduler&lt;/a&gt;&amp;rdquo;: this is responsible for queuing new session requests, handling retries, and waiting until nodes become available. This class is thread-safe and designed to be the main entry point.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/scheduler/Distributor.java&#34;&gt;Distributor&lt;/a&gt;&amp;rdquo;, which is solely responsible for ranking and ordering available hosts, and the sessions that can run on them.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/scheduler/Host.java&#34;&gt;Host&lt;/a&gt;&amp;rdquo; abstraction, which represents a physical place where sessions can be run. Each of these has a number of&amp;hellip;.&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/0e55bfd8d894d450f0ffaeac7d60dd1851334ec7/java/server/src/org/openqa/selenium/remote/server/SessionFactory.java&#34;&gt;Session Factory&lt;/a&gt;&amp;rdquo;, which is responsible for creating a new session.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The scheduler will sit within the new session pipeline. For the standalone server, we just add a single host. For the grid, we can add an arbitrary number of hosts (and therefore session factories)&lt;/p&gt;
&lt;p&gt;As well as the new scheduler, we&amp;rsquo;re preparing the 3.9 release. It should be out next week, if everything goes according to plan. :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Selenium Server &amp; Creating New Sessions</title>
      <link>https://www.rocketpoweredjetpants.com/2018/01/the-selenium-server-creating-new-sessions/</link>
      <pubDate>Mon, 15 Jan 2018 11:18:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/01/the-selenium-server-creating-new-sessions/</guid>
      <description>&lt;p&gt; I&amp;rsquo;ve had the pleasure of  being a co-editor of the W3C&amp;rsquo;s &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html&#34;&gt;WebDriver spec&lt;/a&gt;, as well as the original author and one of the current maintainers of Selenium&amp;rsquo;s Java bindings, and one of the main authors of the current Selenium Server, particularly the pieces to do with implementing the W3C spec. So, as one of the few people on the planet who knows how all the pieces fit together, and &lt;em&gt;why&lt;/em&gt; they fit together that way, I thought it might be helpful to explain how and why the Selenium Server handles a request to &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#new-session&#34;&gt;create a new session&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For this discussion, I&amp;rsquo;ll use the &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#nodes&#34;&gt;terminology&lt;/a&gt; from the spec. A &amp;ldquo;remote end&amp;rdquo; refers to the Selenium Server, and a &amp;ldquo;local end&amp;rdquo; are the language bindings you&amp;rsquo;re probably familiar with &amp;mdash; there&amp;rsquo;s some in all the major programming languages, and about a million of them in the JS space too.&lt;/p&gt;
&lt;p&gt;First of all, it&amp;rsquo;s advisable for the local end to send a single request for a new session that includes the expected payloads for both the &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#new-session&#34;&gt;W3C&lt;/a&gt; and the &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#session-1&#34;&gt;JSON Wire Protocol&lt;/a&gt; dialects at the same time.&lt;/p&gt;
&lt;p&gt;Consider the case where you just send the w3c payload ({&amp;ldquo;capabilities&amp;rdquo;: {&amp;ldquo;browserName&amp;rdquo;: &amp;ldquo;chrome&amp;rdquo;}}). In this case, a w3c server would correctly attempt to start a chrome session. However, a server that only obeys the JSON Wire Protocol will see an empty payload, in which case it&amp;rsquo;s free to do whatever it wants.&lt;/p&gt;
&lt;p&gt;Sending just the JSON Wire Protcol payload ({&amp;ldquo;desiredCapabilities&amp;rdquo;: {&amp;ldquo;browserName&amp;rdquo;: &amp;ldquo;firefox&amp;rdquo;}}) will create a firefox session in a server that understands the JSON Wire Protocol, but will cause a &amp;ldquo;no session created&amp;rdquo; error in a W3C compliant server (since that expects at least {&amp;ldquo;capabilities&amp;rdquo;: {}} to be set).&lt;/p&gt;
&lt;p&gt;So, we have the expected, legal behaviour of the remote end layed out.&lt;/p&gt;
&lt;p&gt;For historical reasons, most bindings only accept a &amp;ldquo;&lt;a href=&#34;https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#capabilities-json-object&#34;&gt;desired capabilities&lt;/a&gt;&amp;rdquo; hash as the argument when creating a new driver instance. Converting the old-style payloads to legal W3C ones is a non-trivial exercise (for example, {&amp;ldquo;firefox_profile&amp;rdquo;: &amp;ldquo;sdfgh&amp;rdquo;} is now {&amp;ldquo;moz:firefoxOptions&amp;rdquo;: {&amp;ldquo;profile&amp;rdquo;: &amp;ldquo;sdfgh&amp;rdquo;}}, but what happens if both are set? Also &amp;ldquo;platform&amp;rdquo; has become &amp;ldquo;platformName&amp;rdquo;, but do the values match? Probably only at the OS family level, according to the &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#dfn-matching-capabilities&#34;&gt;note in the spec&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Most local end bindings get this mapping wrong, but the user doesn&amp;rsquo;t care &lt;em&gt;why&lt;/em&gt; their session isn&amp;rsquo;t as they&amp;rsquo;d expected it to be, they just know &lt;em&gt;it&amp;rsquo;s not right&lt;/em&gt;. What to do? What, my friends, do we do?&lt;/p&gt;
&lt;p&gt;The answer is to be generous about what we receive from the user and attempt to do what they want. Knowing that most local ends have at least a few problems converting the old format to the new format, the selenium server &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/blob/selenium-3.8.1/java/client/src/org/openqa/selenium/remote/NewSessionPayload.java#L312&#34;&gt;creates an ordered list of capabilities&lt;/a&gt;, putting the OSS ones at the front of the list to ensure maximum compatability.&lt;/p&gt;
&lt;p&gt;So, now you know.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why Use a Monorepo?</title>
      <link>https://www.rocketpoweredjetpants.com/2018/01/why-use-a-monorepo/</link>
      <pubDate>Mon, 15 Jan 2018 10:56:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2018/01/why-use-a-monorepo/</guid>
      <description>&lt;p&gt;A monorepo helps reduce the cost of software development. It does this in three different ways: by being simpler to use, by providing better discoverability, and by allowing atomicity of updates. Taking each of these in turn….&lt;/p&gt;
&lt;h3 id=&#34;simplicity&#34;&gt;Simplicity&lt;/h3&gt;
&lt;p&gt;In the ideal world, all you’d need to do is clone your software repository, do a build, make an edit, put up a pull request, and then repeat the last three steps endlessly. Your CI system would watch the repository (possibly a directory or two within it), and kick off builds as necessary. Anything more is adding overhead and cost to the process.&lt;/p&gt;
&lt;p&gt;That overheard starts being introduced when multiple separate codebases  need to be coordinated in some way. Perhaps there’s a protocol definition file that needs to be shared by more than one project. Perhaps there’s utility code that’s shared between more than one project.&lt;/p&gt;
&lt;p&gt;In many organisations developers may not have the ability to set up a repo on demand, so there’s a time and political cost in creating one. Then there’s the ongoing cost of maintaining them, backing them up, and so on. Especially if data is being duplicated between repositories, the aggregate total space used by these repos will also be larger.&lt;/p&gt;
&lt;p&gt;Multiple repositories are not necessarily “simple”.&lt;/p&gt;
&lt;p&gt;One straw man solution to the problems of coordination is to copy all required dependencies into your own repo, but then we’ve a huge pile of duplicated work that opens up the possibility of parallel but incompatible changes being made at the same time.&lt;/p&gt;
&lt;p&gt;A better solution is to build binary artefacts that are stored in some central location, and grab those when required. Bad experiences with storing binaries in the VCS make many people shy of just checking in the artefacts, so this storage solution seems attractive. But the alternatives introduce complexity. Where previously we only had to worry about maintaining the uptime of the source control system, there’s now the additional cost of maintaining this binary datastore, and ensuring its uptime too. Worse, in order to preserve historical builds, the binary datastore needs to be immutable after a write. In my experience, rather than being a directory served using nginx or similar, people turn to commercial solutions even when free alternatives are available. The cost of building and running this infrastructure raises the total cost of development.&lt;/p&gt;
&lt;p&gt;Another area where monorepos bring simplicity is when a package or library needs to be extracted from existing code. This process is simple in a monorepo: just create the new directories, possibly after asking permission from someone, and check in. Every other user receives that change with their next update, without needing to re-run tooling to ensure that their patchwork clients are up to date. Outside of a monorepo, the process can be more painful, especially if a new repository is needed for the freshly extracted code.&lt;/p&gt;
&lt;p&gt;Identifying every place that is impacted by such a code change is also easy in a monorepo, even if you’re not using a graph-based build tool such as &lt;a href=&#34;https://bazel.build/&#34;&gt;bazel&lt;/a&gt; or &lt;a href=&#34;http://buckbuild.com/&#34;&gt;buck&lt;/a&gt;, but doing something like “&lt;a href=&#34;https://paulhammant.com/2017/02/08/further-experiments-with-expanding-contracting-monorepos/&#34;&gt;maven in a monorepo&lt;/a&gt;”. The graph-based build tools typically have a mechanism to query the build graph, but if the tree is one place and you don’t have code-insight tools, then even “grep” can get you quite far.&lt;/p&gt;
&lt;p&gt;There are arguments about monorepos stressing source control software, requiring different tool chains, or not being compatible with CI systems. I addressed those concerns in &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2017/11/tooling-for-monorepos.html&#34;&gt;an earlier post,&lt;/a&gt; but the TL;DR is “modern DVCS systems can cope with the large repos, you don’t need to change how you build code, and your CI pipelines can be left essentially ‘as is’.”&lt;/p&gt;
&lt;h3 id=&#34;discoverability&#34;&gt;Discoverability&lt;/h3&gt;
&lt;p&gt;One of the ways that monorepos drive down the cost of software development is by reducing duplication of effort.&lt;/p&gt;
&lt;p&gt;It’s a truism that the best code is the code that is never written. Every line of code that’s written imposes an ongoing cost of maintenance that needs to be paid until the code is retired from production (at the very earliest!). Fortunately, a good software engineer is a &lt;a href=&#34;http://wiki.c2.com/?LazinessImpatienceHubris&#34;&gt;lazy software engineer&lt;/a&gt; &amp;mdash; if they’re aware of a library or utility that can be used, they’ll use that.&lt;/p&gt;
&lt;p&gt;In order to function properly, a monorepo needs to be structured to ease discoverability of reusable components, as covered in the post about &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2017/11/organising-monorepo.html&#34;&gt;organising a monorepo&lt;/a&gt;. One of the key supporting mechanisms is to separate the tree into functional areas. However, just because a monorepo is structured to aid discoverability, it doesn’t do anything to prevent “spaghetti dependencies” from appearing. What it does do is help surface these dependencies, which would exist in any case, without fancy additional tooling.&lt;/p&gt;
&lt;p&gt;Naturally, a monorepo isn’t the only way of solving the problem of discovering code. Good code insight tooling can fill the same role (go &lt;a href=&#34;https://kythe.io/&#34;&gt;Kythe&lt;/a&gt;!), as do central directories where people can find the code repositories that house useful shared code. Even hearsay and guesswork can suffice; after all, the Java world has coped with Maven Central for an incredibly long time.&lt;/p&gt;
&lt;p&gt;Discovering code has other benefits. As a concrete example, it becomes possible to accurately scope the size of refactorings to APIs within an organisation: simply traverse the graph of everything impacted by a change, and make the change. What used to be a finger-in-the-air guess, or would require coordination across multiple repositories, becomes a far simpler exercise to measure. To actually perform the change? Well, there’s still politics to deal with. Nothing stops that.&lt;/p&gt;
&lt;p&gt;Being able to identify all the locations that are impacted by any change makes CI infrastructure easier to write and maintain. After all, we use CI to answer the questions “is our code safe to deploy? And if not, why is it not safe?” In a monorepo, the graph of dependencies is easier to discover, and that graph can (and should!) be used to drive minimally-sized but provably correct builds, running all necessary build and test and not a single thing more. Needless to say, this means that less work is done by the CI servers, meaninging tighter feedback loops, and faster progress. Do you need a monorepo to build this graph? Of course not. Is building that infrastructure to replicate this something you’ve time to do? Probably not.&lt;/p&gt;
&lt;p&gt;There is also nothing about using a monorepo that precludes putting useful metadata into the tree at appropriate points. Individual parts of the tree can include license information (particularly when importing third party dependencies), or READMEs that provide human-readable information about the purpose of a directory or package, and where to go for help. However, the need for some of this metadata (“how do I get the dependencies?”, “what’s the purpose of this package?”) can be significantly reduced by structuring the monorepo in a meaningful way.&lt;/p&gt;
&lt;h3 id=&#34;atomicity&#34;&gt;Atomicity&lt;/h3&gt;
&lt;p&gt;Occasionally there are components that need to be shared between different parts of the system. Examples include IDL files, protobuf definitions, and other items that can be used to generate code, or must exist as a shared component between client and server.&lt;/p&gt;
&lt;p&gt;Now, there’s reams to be written about how to actually manage updating message definitions in a world where there might be more than one version of that protocol in the wild, and having a monorepo doesn’t prevent you from needing to follow those rules and suggestions. What a monorepo allows is a definitive answer to the question of where these shared items should be. Traditionally, the answer has been:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In one of the clients&lt;/li&gt;
&lt;li&gt;In the server&lt;/li&gt;
&lt;li&gt;In a central location, referenced by everything&lt;/li&gt;
&lt;li&gt;Gadzooks, we’ll copy the damn thing everywhere&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Needless to say, the last approach is remarkably painful, since all changes to the definitions need to tracked across all repositories. In the first two cases, you may end up with unwanted dependencies on either client or server-side code. So the sensible thing to do is to store the shared item in a different repository. This will lead you to the horror of juggling multiple repositories, or, if you’re lucky, taking a dependency on a pre-built binary that someone else is responsible for building.&lt;/p&gt;
&lt;p&gt;Interesting things happen when the shared item needs to be updated. Who is responsible for propagating the changes? Without a requirement to update, teams seldom update dependencies, so there’s out-of-band communication that needs to happen to enforce updates.&lt;/p&gt;
&lt;p&gt;Using a monorepo resolves the problem. There’s one place to store the definition, everyone can depend on it as necessary, and updates happen atomically across the entire codebase (though it may take a long time for those changes to be reflected in production) The same logic applies to making small refactorings — the problem is easy to scope, and completion can be done by an individual working alone.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Monorepos can reduce the cost of software development. They’re not a silver bullet, and they require an organisation to practice at least a minimal level of &lt;a href=&#34;http://www.extremeprogramming.org/rules/collective.html&#34;&gt;collective code ownership&lt;/a&gt;. The approach worked well at Google and Facebook because those companies fostered an attitude that the codebase was a shared resource, that anyone could contribute to and improve.&lt;/p&gt;
&lt;p&gt;For a company which prevents people from viewing everything and having a global view of the source tree, for whatever reason (commercial? Social? Internally competing teams?) a monorepo is a non-starter. That’s a pity, because there are considerable cost savings to be made as more and more share a monorepo. It’s also possible to implement a monorepo where almost everything is public, with parts selected pieces being made available as pre-compiled binaries or otherwise encrypted for most individuals.&lt;/p&gt;
&lt;p&gt;Monorepos help reduce the cost of software over the lifetime of the code by simplifying the path to efficient CI, lowering the overhead of ensuring changes are propagated to dependent projects, and by reducing the effort required to extract new packages and components. As &lt;a href=&#34;https://twitter.com/mexisme&#34;&gt;Will Robertson&lt;/a&gt; pointed out, they can also help reduce the cost of developing development support tooling by providing a single-point “API” to the VCS tool and the source code itself.&lt;/p&gt;
&lt;h3 id=&#34;complementary-practices&#34;&gt;Complementary practices&lt;/h3&gt;
&lt;p&gt;Monorepos solve a whole host of problems, but, just as with any technical solution, there are tradeoffs to be made. Simply cargo-culting what Google, Facebook, or other public early adopters of the pattern have done won’t necessarily lead you to success. On the flip side of the coin, sticking with “business as usual” within a monorepo may not work either.&lt;/p&gt;
&lt;p&gt;Although complex branching strategies might work in a monorepo, the sheer number of moving pieces means that the opportunity for merge conflicts increases dramatically. One of the practices that should be most strongly considered is adopting &lt;a href=&#34;https://trunkbaseddevelopment.com/&#34;&gt;Trunk Based Development&lt;/a&gt;. This also suggests that developers work on short-lived feature branches, hiding work in progress behind &lt;a href=&#34;https://martinfowler.com/articles/feature-toggles.html&#34;&gt;feature flags&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Software development is a social activity. Merging many small commits without describing the logical change going in makes the shared resource of the repo’s logs harder to understand. This leads to a model that is less common than it used to be &amp;mdash; squashing the individual steps that lead to a logical change to a single commit, which describes that logical change. This makes using the commit logs a useful resource too. Code review tools such as &lt;a href=&#34;https://www.phacility.com/phabricator/&#34;&gt;Phabricator&lt;/a&gt; help make this process simpler.&lt;/p&gt;
&lt;p&gt;Most importantly: stop and think. It is unlikely your company is Google, Facebook, Twitter, Uber, or one of the other high-profile large companies that have already adopted monorepos (but if you’re reading from one of those places, “Hi!”). A monorepo makes a lot of sense, but simply &lt;a href=&#34;https://en.wikipedia.org/wiki/Cargo_cult_programming&#34;&gt;aping the big beasts&lt;/a&gt; and hoping for the best won’t lead to happiness. Consider the advantages to your organisation for each step of the path towards a monorepo, and take those steps with your eyes open.&lt;/p&gt;
&lt;h3 id=&#34;thanks&#34;&gt;Thanks&lt;/h3&gt;
&lt;p&gt;Thank you to Nathan Fisher, Josh Graham, Paul Hammant, Felipe Lima, Dan North, Will Robertson, and Chris Stevenson for the suggestions and feedback while I was writing this post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Organising a Monorepo</title>
      <link>https://www.rocketpoweredjetpants.com/2017/11/organising-a-monorepo/</link>
      <pubDate>Wed, 22 Nov 2017 15:39:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2017/11/organising-a-monorepo/</guid>
      <description>&lt;p&gt;How should a monorepo be organised? It only takes a moment to come up with many competing models, but the main ones to consider are “by language”, “by project”, “by functional area”, and “&lt;a href=&#34;https://nixos.org/nix/&#34;&gt;nix&lt;/a&gt; style”. Of course, it’s entirely possible to blend these approaches together. As an example, my preference is “primarily language-based, secondarily by functional area”, but perhaps I should explain the options.&lt;/p&gt;
&lt;h3 id=&#34;language-based-monorepos&#34;&gt;Language-based monorepos&lt;/h3&gt;
&lt;p&gt;These repos contain a top-level directory per language. For languages that are typically organised into parallel test and source trees (I’m looking at you, Java) there might be two top-level directories.&lt;/p&gt;
&lt;p&gt;Within the language specific tree, code is structured in a way that is unsurprising to “native speakers” of that language. For Java, that means a package structure based on fully-qualified domain names. For many other languages, it makes sense to have a directory per project or library.&lt;/p&gt;
&lt;p&gt;Third party dependencies can either be stored within the language-specific directories, or in a separate top-level directory, segmented in the same language specific way.&lt;/p&gt;
&lt;p&gt;This approach works well when there aren’t too many languages in play. Organisation standards, such as those present in Google, may limit the number of languages. Once the number of languages becomes too many, it becomes hard to determine where to start looking for the code you may depend on.&lt;/p&gt;
&lt;h3 id=&#34;project-based-monorepos&#34;&gt;Project-based monorepos&lt;/h3&gt;
&lt;p&gt;One drawback with a language-based monorepo is that it’s increasingly common to use more than one language per project. Rather than spreading code across multiple locations, it’s nice to co-locate everything needed for a particular project in the same directory, with common code being stored “elsewhere”. In this model, therefore, there are multiple top-level directories representing each project.&lt;/p&gt;
&lt;p&gt;The advantage with this approach is that creating a sparse checkout is incredibly simple: just clone the top-level directory that contains the project, et voila! Job done! It also makes removing dead code simple &amp;mdash; just delete the project directory once it’s no longer needed, and everything is gone. This same advantage means that it’s easy to export a &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2017/11/some-useful-monorepo-definitions.html&#34;&gt;cell&lt;/a&gt; as an Open Source project.&lt;/p&gt;
&lt;p&gt;The disadvantage with project-based monorepos is that the top level can quickly become bloated as more and more projects are added. Worse, there&amp;rsquo;s the question of what to do when projects are mostly retired, or have been refactored to mostly slivers of their former glory.&lt;/p&gt;
&lt;h3 id=&#34;functional-area-based-monorepos&#34;&gt;Functional area-based monorepos&lt;/h3&gt;
&lt;p&gt;A key advantage of monorepos is “discoverability”. It’s possible to organise a monorepo to enhance this, by grouping code into functional areas. For example, there might be a directory for “crypto” related code, another for “testing”, another for “networking” and so on. Now, when someone is looking for something they just need to consider the role it fulfills, and look at the tree to identify the target to depend on.&lt;/p&gt;
&lt;p&gt;One way to make this approach fail miserably is to make extensive use of code names. “Loki” may seem like a cool project name (it’s not), but I’ll be damned if I can tell what it actually does without asking someone. Being developers, we need snazzy code names at all times, and by all means organise teams around those, but the output of those projects should be named in as a vanilla a way as possible: the output of “loki” may be a “man in the middle ssl proxy”, so stick that in “networking/ssl/proxy”. Your source tree should be painted beige &amp;mdash; the least exciting colour in the world.&lt;/p&gt;
&lt;p&gt;Another problem with the functional area-based monorepos is that considerable thought has to be put into their initial structure. Moving code around is possible (and possible atomically), but as the repo grows larger the structure tends to ossify, and considerable social pressure needs to be overcome to make those changes.&lt;/p&gt;
&lt;h3 id=&#34;nix-style-monorepos&#34;&gt;Nix-style monorepos&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://nixos.org/nix/about.html&#34;&gt;Nix&lt;/a&gt; is damn cool, and offers many capabilities that are desirable for a monorepo being run in a low-discipline (or high-individuality) engineering environment, incapable of managing to keep to only using (close to a) single version of each dependency. Specifically, a nix-based monorepo actively supports multiple versions of dependencies, with projects depending on specific versions, and making this clear in their build files.&lt;/p&gt;
&lt;p&gt;This differs from a regular monorepo with a few alternate versions of dependencies that are particularly taxing to get onto a single version (*cough* &lt;a href=&#34;https://www-01.ibm.com/software/globalization/icu/&#34;&gt;ICU&lt;/a&gt; *cough*) because multiple versions of things are actively encouraged, and dependencies need to be more actively managed.&lt;/p&gt;
&lt;p&gt;There are serious maintainability concerns when using the nix-style monorepo, especially for components that need to be shared between multiple projects. Clean up of unused cells, mechanisms for migrating projects as dependencies update, and stable and fast constraint solving all need to be in place. Without those, a nix-style monorepo will rapidly become an ungovernable mess.&lt;/p&gt;
&lt;p&gt;The maintainability issue is enough to make this a particularly poor choice. Consider this the “anti-pattern” of monorepo organisation.&lt;/p&gt;
&lt;h3 id=&#34;blended-monorepos&#34;&gt;Blended monorepos&lt;/h3&gt;
&lt;p&gt;It’s unlikely that any monorepo would be purely organised along a single one of these lines; a hybrid approach is typically simpler to work with. These “blended monorepos” attempt to address the weaknesses of each approach with the strengths of another.&lt;/p&gt;
&lt;p&gt;As an example, project-based monorepos rapidly have a cluttered top-level directory. However, by splitting by functional area, or language and then functional area, the top-level becomes less cluttered and simultaneously easier to navigate.&lt;/p&gt;
&lt;p&gt;For projects or dependencies that are primarily in one language, but with support libraries for other languages, take a case-by-case approach. For something like MySQL, it may make sense to just shovel everything into “c/database/mysql”, since the java library (for example) isn’t particularly large. For other tools, it may make more sense to separate the trees and stitch everything together using the build tool.&lt;/p&gt;
&lt;h3 id=&#34;third-party-dependencies&#34;&gt;Third party dependencies&lt;/h3&gt;
&lt;p&gt;There is an interesting discussion to be had about where and how to store third party code. Do you take binary dependencies, or pull in the source? Do you store the third party code in a separate third party directory, or alongside first party code? Do you store the dependencies in your repository at all, or push them to something like a Maven artifact repository.&lt;/p&gt;
&lt;p&gt;The temptation when checking in the source is that it becomes very easy to accidentally start maintaining a fork of whichever dependency it is. After all, you find a bug, and it’s sooo easy to fix it in place and then forget (or not be allowed) to upstream the fix. The advantage of checking in the source is that you can build from source, allowing you to optimise it as along with the rest of the build. Depending on your build tool, it may be possible to only rely on those parts of the library that are actually necessary for your project.&lt;/p&gt;
&lt;p&gt;Checking in the binary artifacts has the disadvantage that source control tools are seldom optimised for storing binaries, so any changes will cause the overall size of the repository to grow (though not a snapshot at a single point in time) The advantage is that build times can be significantly shorter (as all that needs to be done is link the dependency in)&lt;/p&gt;
&lt;p&gt;Binary dependencies pulled from third parties can be significantly easier to update. Tools such as maven, nuget, and cocoapods can describe a graph of dependencies, and these graphs can be reified by committing them to your monorepo (giving you stable, repeatable historical builds) or left where they lie and pulled in at build time. As one of the reviewers of this post pointed out, this requires the community the binaries are being pulled from to be well managed: releases must not be overwritten (which can be verified by simple hash checks), and snapshots should be avoided.&lt;/p&gt;
&lt;p&gt;Putting labels on these, there are in-tree dependencies and externally managed dependencies, and both come in source and binary flavours.&lt;/p&gt;
&lt;h3 id=&#34;thanks&#34;&gt;Thanks&lt;/h3&gt;
&lt;p&gt;My thanks to Nathan Fisher, Josh Graham, Will Robertson, and Chris Stevenson for their feedback while writing this post. Some of the side conversations are worth a post all of their own!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Tooling for Monorepos</title>
      <link>https://www.rocketpoweredjetpants.com/2017/11/tooling-for-monorepos/</link>
      <pubDate>Mon, 20 Nov 2017 14:43:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2017/11/tooling-for-monorepos/</guid>
      <description>&lt;p&gt;One argument against monorepos is that you need special tooling to make them work. This argument commonly gets presented in a variety of ways, but the most frequent boil down to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Code size: a single repo would be too big for our source control system!&lt;/li&gt;
&lt;li&gt;Requirement for specialised tooling: we&amp;rsquo;re happy with what we have!&lt;/li&gt;
&lt;li&gt;Reduces the ability of teams to move fast and independently&lt;/li&gt;
&lt;li&gt;Politics and fiefdoms&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s take each of these in turn.&lt;/p&gt;
&lt;h3 id=&#34;code-size&#34;&gt;Code size&lt;/h3&gt;
&lt;p&gt;Most teams these days are using some form of DVCS, with git being the most popular. Git was designed for use with the Linux kernel, so initially scaled nicely for that use-case, but started to get painful after that. That means that we start with some pretty generous limits: a fresh clone of linux repo at depth 1 takes just shy of 1GB of code spread between in over 60K files (&lt;a href=&#34;http://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html&#34;&gt;here’s how they make it work&lt;/a&gt;!). Even without modifying stock git, Facebook was able to get their &lt;a href=&#34;https://twitter.com/feross/status/459259593630433280&#34;&gt;git repo up to 54GB&lt;/a&gt; (admittedly, with only 8GB of code). MS have scaled Git to the entire &lt;a href=&#34;https://blogs.msdn.microsoft.com/bharry/2017/05/24/the-largest-git-repo-on-the-planet/&#34;&gt;Windows codebase&lt;/a&gt;: that’s 300GB spread between 3.5M files and hundreds of branches. Their git extensions are &lt;a href=&#34;https://blogs.msdn.microsoft.com/devops/2017/11/15/updates-to-gvfs/&#34;&gt;now coming to GitHub and non-Windows platforms&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which is good news! Your source control system of choice can cope with the amount of code a monorepo contains. Hurrah!&lt;/p&gt;
&lt;p&gt;But how long does that take to check out? I’ll be honest, checking out a repo that’s 1GB large can take a while. If that is, you check out the whole 1GB. Git, Mercurial, Perforce, and Subversion support “sparse” working copies, where you only clone those directories you need. The sparse checkout declarations can either be declared in files stored in source control, or they can computed. They likely follow &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2017/11/some-useful-monorepo-definitions.html&#34;&gt;cell&lt;/a&gt; boundaries within the monorepo. It should be clear that in the ideal case, the end result is a working copy exactly the same size as a hand-crafted repository containing just what’s needed, and nothing more. As a developer moves from project to project, or area to area, they can &lt;a href=&#34;https://paulhammant.com/categories.html#Expanding_and_Contracting_Monorepos&#34;&gt;expand or contract&lt;/a&gt; their current clone to exactly match their needs.&lt;/p&gt;
&lt;p&gt;So your checkouts don’t necessarily get larger. They may even get smaller.&lt;/p&gt;
&lt;p&gt;But, what if you do have everything checked out? Your source control tool needs to know which files have changed. As the size of the repository grows, the slower these operations become, impacting developer performance. Except both Git and Mercurial have support for filesystem watching daemons (notably “&lt;a href=&#34;https://facebook.github.io/watchman/&#34;&gt;watchman&lt;/a&gt;”) These allow file checking operations to scale linearly with the number of files changed, rather than with the number of files in the repository (I’d hope that even those using a “normal” large checkout would consider using this)&lt;/p&gt;
&lt;p&gt;So everything is fine with the raw tooling. But what about your IDE?&lt;/p&gt;
&lt;p&gt;I mean, yeah, if you’ve checked out the entire source tree, surely your IDE will grind to a halt? First of all, don’t do that &amp;mdash; use a sparse clone &amp;mdash; but if you insist on doing it, update your tooling. Facebook spent a chunk of resources to help make &lt;a href=&#34;http://www.jetbrains.com/idea/&#34;&gt;IntelliJ&lt;/a&gt; more efficient when dealing with large projects, and upstreamed those changes to Jetbrains, who accepted the patches. It was possible to pull in the source code for every Facebook Android app at the same time in IntelliJ. You may have a lot of code, but it’s unlikely to be that much. Other editors can also happily work with large source trees.&lt;/p&gt;
&lt;p&gt;So, code size isn’t the problem you might imagine it is.&lt;/p&gt;
&lt;h3 id=&#34;requirement-for-specialised-tooling&#34;&gt;Requirement for specialised tooling&lt;/h3&gt;
&lt;p&gt;Quite often when people talk about monorepos, they also talk about the exotic tooling they use, from custom build systems, tricked-out source control servers, and custom CI infrastructure. Perhaps a giant company has the time and resources to build that, but you’re too busy doing your own work.&lt;/p&gt;
&lt;p&gt;Except a monorepo doesn’t require you to do any of those things. Want to use a recursive build tool you’re already familiar with? Go ahead. Paul Hammant has done some interesting work demonstrating how it’s possible to &lt;a href=&#34;https://paulhammant.com/2017/01/27/maven-in-a-google-style-monorepo/&#34;&gt;use&lt;/a&gt;  &lt;a href=&#34;https://paulhammant.com/2017/02/08/further-experiments-with-expanding-contracting-monorepos/&#34;&gt;maven&lt;/a&gt; (and, by extension, gradle and make) in a monorepo.&lt;/p&gt;
&lt;p&gt;Switching to a build tool such as &lt;a href=&#34;http://buckbuild.com/&#34;&gt;buck&lt;/a&gt; or &lt;a href=&#34;https://bazel.build/&#34;&gt;bazel&lt;/a&gt; does make using a monorepo simpler, because these tools provide mechanisms to query the build graph, and can be simply configured to mark various parts of the tree as being visible or not to particular rules, but using one of these isn’t required. One nice thing? You don’t need to write buck or bazel yourself &amp;mdash; they’re both already out there and available for you to use.&lt;/p&gt;
&lt;p&gt;Similarly, if you’re comfy with &lt;a href=&#34;https://jenkins.io/&#34;&gt;jenkins&lt;/a&gt; or &lt;a href=&#34;https://travis-ci.org/&#34;&gt;travis&lt;/a&gt;, continue using them. Admittedly, you’ll need to configure the CI builds to watch not just a repo, but a subdirectory within a repo, but that’s not too hard to do. If you’re using a graph-based build tool, then you can even use jenkins or &lt;a href=&#34;https://buildbot.net/&#34;&gt;buildbot&lt;/a&gt; to identify the minimal set of items to rebuild and test, but, again, there’s no need to do that. Just keep on trucking the way you do now.&lt;/p&gt;
&lt;h3 id=&#34;reduces-the-ability-of-teams-to-move-fast-and-independently&#34;&gt;Reduces the ability of teams to move fast and independently&lt;/h3&gt;
&lt;p&gt;Having a repository per-project or per-team allows them to operate entirely independently of one another. Except that’s not true unless you’re writing every single line of code yourself. It’s likely you have at least a few first and third party dependencies. At some point, those dependencies really should be updated. Having your own repo means that you can pick the timing, but it also means you have to do the work.&lt;/p&gt;
&lt;p&gt;Monorepos naturally lead people to minimising the number of versions of third party dependencies towards one, if only to avoid nasty &lt;a href=&#34;https://en.wikipedia.org/wiki/Dependency_hell&#34;&gt;diamond dependency issues&lt;/a&gt;, but there’s no technical reason why there can’t be more than one version of a library in the tree. Of course, only a narcissist would check in a library without making an effort to remove the old versions. There are a pile of ways to do this, but my preferred way is to say that the person wanting the update manages the update, and asks for help from teams that are impacted by the change. I’ll cover the process in a later post. No matter how it’s done, the effect of having a single atomic change amortises the cost of the change over all the repos, reducing the cost of software development across the entire organisation by front loading the cost of making the change.&lt;/p&gt;
&lt;p&gt;But perhaps it’s not the dependencies you enjoy freedom on. Perhaps it’s the choice of language and tooling? There’s no reason a &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2017/11/organising-monorepo.html&#34;&gt;properly organised monorepo&lt;/a&gt; can’t support multiple languages (pioneers such as &lt;a href=&#34;https://www.quora.com/Which-programming-languages-does-Google-use-internally&#34;&gt;Google&lt;/a&gt; and Facebook have mixed language repos) Reducing the number of choices may be an organisation-level goal, in order to allow individuals to cycle quickly and easily between teams (which is why we have code style guidelines, right?), but there’s nothing about using a monorepo that prevents you from using many different tool chains.&lt;/p&gt;
&lt;p&gt;As a concrete example of this, consider Mozilla. They’re a remote-first, distributed team of iconoclasts and lovely folks (the two aren’t mutually exclusive :) ) Mozilla-central houses a huge amount of code, from the browser, through extensions, to testing tools, and a subset of the web-platform-tests. A host of different languages are used within that tree, including Python, C/C++, Rust, Javascript, Java, and Go, and I’m sure there are others too. Each team has picked what’s most appropriate and run with those.&lt;/p&gt;
&lt;h3 id=&#34;politics-and-fiefdoms&#34;&gt;Politics and fiefdoms&lt;/h3&gt;
&lt;p&gt;There’s no getting away from politics and fiefdoms. Sorry folks. Uber have stated that one of the reasons they prefer many separate repositories is to help &lt;a href=&#34;http://highscalability.com/blog/2016/10/12/lessons-learned-from-scaling-uber-to-2000-engineers-1000-ser.html&#34;&gt;reduce the amount of politics&lt;/a&gt;. However, hiding from things is seldom the best way to deal with them, and the technical benefits of using a monorepo can be compelling, as &lt;a href=&#34;https://eng.uber.com/ios-monorepo/&#34;&gt;Uber have found&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If an organisation enthusiastically embraces the concept of &lt;a href=&#34;http://wiki.c2.com/?CollectiveCodeOwnership&#34;&gt;collective code ownership&lt;/a&gt;, it’s possible to avoid anything other than purely social constructs to prevent ego being bruised and fiefdoms being encroached on. The only gateways to contribution become those technical gateways placed to ensure code quality, such as code review.&lt;/p&gt;
&lt;p&gt;Sadly, not many companies embrace collective code ownership to that extent. The next logical step is apply something like GitHub’s “&lt;a href=&#34;https://github.com/blog/2392-introducing-code-owners&#34;&gt;code owners&lt;/a&gt;”, where owners are notified of changes before they are committed (ideally. Using post-commit hooks for after the fact notification isn’t as efficient) A step further along, and OWNERS files (as seen in &lt;a href=&#34;https://www.chromium.org/developers/contributing-code#TOC-Code-review&#34;&gt;Chromium’s source tree&lt;/a&gt;) list individuals and team aliases that are required to give permission to land code.&lt;/p&gt;
&lt;p&gt;If there is really strong ownership of code, then your source control system may be able to help. For example, perforce allows &lt;a href=&#34;https://www.perforce.com/perforce/r15.1/manuals/p4sag/chapter.protections.html&#34;&gt;protection levels&lt;/a&gt; to be set for individual directories within a tree, and pre-commit hooks can be used for a similar purpose with other source control systems.&lt;/p&gt;
&lt;h3 id=&#34;getting-the-most-of-a-monorepo&#34;&gt;Getting the most of a monorepo&lt;/h3&gt;
&lt;p&gt;Having said that you don&amp;rsquo;t need to change much to start using a monorepo, there are patterns that allow one to be used efficiently. These suggestions can also be applied to any large code repositories: after all, as Chris Stevenson said “any sufficiently complicated developer workspace contains an ad-hoc, informally specified, bug-ridden implementation of half a monorepo”&lt;/p&gt;
&lt;p&gt;Although it’s entirely possible to use recursive build tools with a monorepo (early versions of Google’s still used make), moving to a graph-based build tool is one of the best ways to take advantage of a monorepo.&lt;/p&gt;
&lt;p&gt;The first reason is simply logistical. The two major graph-based build tools (&lt;a href=&#34;http://buckbuild.com/&#34;&gt;Buck&lt;/a&gt; and &lt;a href=&#34;https://bazel.build/&#34;&gt;Bazel&lt;/a&gt;) both support the concept of “&lt;a href=&#34;https://buckbuild.com/concept/visibility.html&#34;&gt;visibility&lt;/a&gt;”. This makes it possible to segment the tree, marking public-facing APIs as such, whilst allowing teams to limit who can see the implementations. Who can depend on a particular target is defined by the target itself, not by its consumers, preventing uncontrolled growth in access to internal details. An OOP developer is already familiar with the concept of visibility, and the same ideas apply, scaled out to the entire tree of code.&lt;/p&gt;
&lt;p&gt;The second reason is practical. The graph-based build tools frequently have a query language that can be used to quickly identify targets given certain criteria. One of those criteria might be “given this file has changed, identify the targets that need to be rebuilt”. This simplifies the process of building a sensible, scalable CI system from building blocks such as &lt;a href=&#34;https://buildbot.net/&#34;&gt;buildbot&lt;/a&gt; or &lt;a href=&#34;https://www.gocd.org/&#34;&gt;GoCD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another pattern that’s important for any repository that has many developers hacking on it simultaneously is having a mechanism to serialise commits to the tree. Facebook have &lt;a href=&#34;https://atscaleconference.com/videos/scaling-up-job-scheduling-with-a-matchmaking-service/&#34;&gt;spoken about this publicly&lt;/a&gt;, and do so with their own tooling, but something like &lt;a href=&#34;https://www.gerritcodereview.com/#manage-workflows&#34;&gt;gerrit&lt;/a&gt;, or even a continuous build could handle this. Within a monorepo, this tooling doesn’t need to be in place from the very beginning, and may never be needed, but be aware that it eases the problem of commits not being able to land in areas of high churn.&lt;/p&gt;
&lt;p&gt;A final piece in the tooling puzzle is to have a continuous build tool that’s capable of watching individual directories rather than the entire repository. Alternatively, using a graph-based build tool allows a continuous build that watches the entire repository to at least target the minimal set of targets that need rebuilding. Of course, it’s entirely possible to place the continuous build before the tooling that serialises the commits, so you always have a green HEAD of master….&lt;/p&gt;
&lt;h3 id=&#34;thanks&#34;&gt;Thanks&lt;/h3&gt;
&lt;p&gt;My thanks to Nathan Fisher, Josh Graham, Paul Hammant, Will Robertson, and Chris Stevenson for their feedback and comments while writing and editing this post. Without their help, this would have rambled across many thousands of words.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Some Useful Monorepo Definitions</title>
      <link>https://www.rocketpoweredjetpants.com/2017/11/some-useful-monorepo-definitions/</link>
      <pubDate>Sun, 19 Nov 2017 23:14:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2017/11/some-useful-monorepo-definitions/</guid>
      <description>&lt;p&gt;The concept of a monorepo seems so self-evident that there is little need to define it. Just co-locate all your code in one place, and you’re done, right?&lt;/p&gt;
&lt;p&gt;The problem is that this doesn’t capture lots of the nuance of the term. After all, if all you have is a single project, then, by this definition, you have a monorepo. While technically correct (&lt;a href=&#34;https://theinfosphere.org/Number_1.0&#34;&gt;the best kind of correct!&lt;/a&gt;) this doesn’t feel right. There has to be more to it than that.&lt;/p&gt;
&lt;h3 id=&#34;monorepo&#34;&gt;Monorepo&lt;/h3&gt;
&lt;h4 id=&#34;summary&#34;&gt;Summary:&lt;/h4&gt;
&lt;p&gt;A monorepo represents the body of code and supporting digital assets owned by an organisation. Within that body of code, it’s possible to draw logical boundaries around certain areas, either shared libraries, individual projects, or other groupings.&lt;/p&gt;
&lt;h4 id=&#34;discussion&#34;&gt;Discussion:&lt;/h4&gt;
&lt;p&gt;Previously, I’ve written that a monorepo is “&lt;a href=&#34;https://www.rocketpoweredjetpants.com/2015/04/monorepo-one-source-code-repository-to.html&#34;&gt;a unified source code repository used by an organisation to host as much of its code as possible.&lt;/a&gt;” That does the job, but I think it falls short of succinctly describing the goals of a monorepo in favour of an implementation of the pattern. Oh well, exploration of an idea is an iterative process, with each iteration being able to use the insights from previous iterations. Let’s iterate again!&lt;/p&gt;
&lt;h3 id=&#34;cell&#34;&gt;Cell&lt;/h3&gt;
&lt;h4 id=&#34;summary-1&#34;&gt;Summary:&lt;/h4&gt;
&lt;p&gt;A cell is an atomic unit representing a single logical piece within the monorepo.&lt;/p&gt;
&lt;h4 id=&#34;discussion-1&#34;&gt;Discussion:&lt;/h4&gt;
&lt;p&gt;When we were working on &lt;a href=&#34;https://buckbuild.com/&#34;&gt;Buck&lt;/a&gt;, we struggled for a long time to come up with the best name for the logical areas with the monorepo. Initially, they were formed from the individual repositories we were coalescing into the monorepo. However, “repository” was an overloaded term, and so one we wanted to avoid. Similarly, “module” already has established meaning in some of languages we wanted to support.&lt;/p&gt;
&lt;p&gt;In the end, we settled on using a biological metaphor. Because a monorepo represents a body of code, and these logical groupings represent the atomic units that the monorepo is constructed from, we called them cells. In many organisations, pre-monorepo, a cell represents a single repository.&lt;/p&gt;
&lt;p&gt;Because of this mapping to a conceptual repository, a cell is a great candidate for Open Sourcing. Should this happen, it’s entirely possible that there needs to be some tooling to map file structure from the shape used within the monorepo to the shape expected by the OSS library. Ideally, that tooling would allow code to be both imported and exported to and from the monorepo, rather than only allowing a push in a single direction.&lt;/p&gt;
&lt;h3 id=&#34;projected-monorepo&#34;&gt;Projected Monorepo&lt;/h3&gt;
&lt;h4 id=&#34;summary-2&#34;&gt;Summary:&lt;/h4&gt;
&lt;p&gt;A set of repositories presented as if they were a monorepo, typically via additional tooling.&lt;/p&gt;
&lt;h4 id=&#34;discussion-2&#34;&gt;Discussion:&lt;/h4&gt;
&lt;p&gt;Monorepos may be classified by the way that the code within is organised, but there is another approach: the projected monorepo. This isn’t a monorepo in the (umm…) traditional sense, where all the code is in the same code repository, but something that acts as if it were a monorepo through external tooling. An example would be the &lt;a href=&#34;https://source.android.com/&#34;&gt;Android Open Source&lt;/a&gt; project, which uses “&lt;a href=&#34;https://android.googlesource.com/tools/repo/&#34;&gt;repo&lt;/a&gt;” to stitch together multiple separate repositories into something that acts as a single cohesive whole. To a lesser extent, things like git submodules also fulfill the same role of creating projected monorepos.&lt;/p&gt;
&lt;p&gt;In a projected monorepo, it is clear where the cells lie &amp;mdash; they’re the individual repositories that are being stitched together to form the new whole.&lt;/p&gt;
&lt;h3 id=&#34;target&#34;&gt;Target&lt;/h3&gt;
&lt;h4 id=&#34;summary-3&#34;&gt;Summary:&lt;/h4&gt;
&lt;p&gt;The individual units addressable by the build tool, which are used to declare dependencies.&lt;/p&gt;
&lt;h4 id=&#34;discussion-3&#34;&gt;Discussion:&lt;/h4&gt;
&lt;p&gt;Within a monorepo there are targets. These are units that are addressable by the build tool, and are also typically used to declare dependencies. They typically have concrete outputs, such as libraries or binaries. Targets are human-readable, and are most commonly given as a path within the repository.&lt;/p&gt;
&lt;p&gt;A cell is typically composed of many targets. As an example, perhaps a cell consists of a single library. There might be targets within that cell would allow the library to be built, the tests for that library to built, and (perhaps) another to allow those tests to be run.&lt;/p&gt;
&lt;h3 id=&#34;graph-based-build-tool&#34;&gt;Graph-based build tool&lt;/h3&gt;
&lt;h4 id=&#34;summary-4&#34;&gt;Summary:&lt;/h4&gt;
&lt;p&gt;A build tool designed for use within a monorepo where build files are located throughout the source tree and used in a non-recursive manner.&lt;/p&gt;
&lt;h4 id=&#34;discussion-4&#34;&gt;Discussion:&lt;/h4&gt;
&lt;p&gt;It&amp;rsquo;s common to use a graph-based build tool with monorepos. These are tools that are natively designed for a monorepo, and operate on the directed acyclic graph of dependencies between targets. They typically provide the ability to build polyglot projects, and the ability to query the build graph. The two major examples are Google’s &lt;a href=&#34;https://bazel.build/&#34;&gt;bazel&lt;/a&gt; and Facebook’s &lt;a href=&#34;http://buckbuild.com/&#34;&gt;buck&lt;/a&gt;. Both of these tools can trace their user-facing design to Google’s “&lt;a href=&#34;https://books.google.co.uk/books?id=_4rPCwAAQBAJ&amp;amp;pg=PA90&amp;amp;dq=Bazel+build&amp;amp;hl=en&amp;amp;sa=X&amp;amp;redir_esc=y#v=onepage&amp;amp;q=Bazel%20build&amp;amp;f=false&#34;&gt;Blaze&lt;/a&gt;” build tool.&lt;/p&gt;
&lt;p&gt;Admittedly, behind the scenes almost every build tool makes use of basic graph theory in order to work: after all, most tools to a topological sort of targets in order to work their magic, and they frequently have commands that allow that graph to be queried. The major difference between these other tools and what I’m terming a “graph-based build tool” is the use of build files throughout the tree that are used in a non-recursive way. This encourages the creation of relatively small compilation units.&lt;/p&gt;
&lt;p&gt;Hopefully these terms, and the various ways of organising a monorepo, give us a common language to discuss monorepos in a meaningful way.&lt;/p&gt;
&lt;h3 id=&#34;thanks&#34;&gt;Thanks&lt;/h3&gt;
&lt;p&gt;My thanks to Kent Beck, Nathan Fisher, Josh Graham, Paul Hammant, Will Robertson, and Chris Stevenson for their comments and feedback while writing this post. The conversations have definitely helped clarify and improve this post.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Month in Selenium - October</title>
      <link>https://www.rocketpoweredjetpants.com/2017/11/a-month-in-selenium-october/</link>
      <pubDate>Sun, 19 Nov 2017 20:58:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2017/11/a-month-in-selenium-october/</guid>
      <description>&lt;p&gt;Another month, another update, you lucky people.  The highlights:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;W3C TPAC.&lt;/strong&gt;&lt;br&gt;
I attended the W3C&amp;rsquo;s TPAC meeting in California. This is the main get-together for many of the &amp;ldquo;working groups&amp;rdquo; that are working on standards as part of the W3C. It&amp;rsquo;s also where the &lt;a href=&#34;https://www.w3.org/testing/browser/&#34;&gt;Browser Tools and Testing Working Group&lt;/a&gt; met to discuss progress on the &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#&#34;&gt;WebDriver spec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Good news! Once we clean up the implementation report, we&amp;rsquo;re ready to move to &amp;ldquo;&lt;a href=&#34;https://www.w3.org/Consortium/Process/Process-19991111/tr.html#RecsPR&#34;&gt;Proposed Recommendation&lt;/a&gt;&amp;rdquo;, which is the last step before becoming a standard (or &amp;ldquo;&lt;a href=&#34;https://www.w3.org/Consortium/Process/Process-19991111/tr.html#RecsW3C&#34;&gt;Recommendation&lt;/a&gt;&amp;rdquo; in W3C parlance).&lt;/p&gt;
&lt;p&gt;More good news! The &amp;ldquo;Level 2&amp;rdquo; version of WebDriver will have a new logging infrastructure added. This will make it easier for you (yes, you!) to figure out where failures have occurred. Better insight should lead to more stable tests.&lt;/p&gt;
&lt;p&gt;Even more good news! Some of the folks from &lt;a href=&#34;https://saucelabs.com/&#34;&gt;Sauce Labs&lt;/a&gt; attended the face-to-face meeting. They help bring an additional perspective to the design and use cases of the protocol. Until now, the group has been mostly composed of browser implementors and people from the Selenium project. The more people involved with the spec, the better it&amp;rsquo;s going to be.&lt;/p&gt;
&lt;p&gt;The minutes for this face-to-face session are &lt;a href=&#34;https://www.w3.org/wiki/WebDriver#Burlingame.2C_CA_November_2017&#34;&gt;available&lt;/a&gt;, as are the minutes for the other face-to-face sessions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hacking on Selenium&lt;/strong&gt;&lt;br&gt;
Last month, we were closing in on the Selenium 3.7 release. This month we shipped 3.7.0 and then, because of a small oversight where we missed a jar file in the downloadable artefacts, &lt;a href=&#34;https://goo.gl/3aWDMc&#34;&gt;3.7.1&lt;/a&gt;. There are some nice changes in there. As mentioned last month, one of the areas of focus has been improving how we handle the &lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#new-session&#34;&gt;New Session&lt;/a&gt; command when dealing with a local end that might speak both the W3C and &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#session&#34;&gt;JSON Wire Protocol&lt;/a&gt; dialects of the webdriver protocol. One of the things that the spec says we&amp;rsquo;re meant to do is pass though additional top-level fields in the new session payload. 3.7.1 now does this (hurrah!)&lt;/p&gt;
&lt;p&gt;One of the nice things from the work in 3.7 is that we&amp;rsquo;ve laid the groundwork for a clean up of the Selenium Grid code. As part of that, we restored a behaviour where a Grid Node, configured with a path for the Firefox or Chrome binary would have this path injected into any capabilities when starting a session. Making the nodes even more configurable is something that&amp;rsquo;s on the road map for a later release.&lt;/p&gt;
&lt;p&gt;More next month!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Month in Selenium - September</title>
      <link>https://www.rocketpoweredjetpants.com/2017/10/a-month-in-selenium-september/</link>
      <pubDate>Wed, 18 Oct 2017 10:46:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2017/10/a-month-in-selenium-september/</guid>
      <description>&lt;p&gt;I realise that this blog has been pretty quiet. Part of the reason for that is that I&amp;rsquo;m terrible at sitting down and just writing. What I really need is an incentive. That incentive arrived this month in the form of the Selenium Fellowship, which takes the form of a stipend to fund work hacking on Selenium. Part of the agreement is a monthly blog post. So, you all have the Software Freedom Conservancy to thank :)&lt;/p&gt;
&lt;p&gt;So, what contributions have I been making to the Selenium project this month?&lt;/p&gt;
&lt;p&gt;There are two major highlights. The first of these is &lt;a href=&#34;http://www.seleniumconf.com/&#34;&gt;Selenium Conf&lt;/a&gt;, which was in Berlin. I gave the &lt;a href=&#34;https://www.youtube.com/watch?v=v9OoXIZobdk&#34;&gt;State of the Union&lt;/a&gt; keynote (so called because the first one was an update of how the merger of the Selenium and WebDriver projects was going) Over the past few Selenium Conferences, the theme has slowly been building that Open Source Software depends on people to move it forward. This time, the message was far starker, as I counted the number of people who contribute to key parts of the project &amp;mdash; for some pieces, we depend on one person alone. I also covered the various moving pieces in the project, using Kent Beck&amp;rsquo;s &amp;ldquo;&lt;a href=&#34;https://www.facebook.com/kentlbeck/notes?lst=655140087%3A612973674%3A1508319708&#34;&gt;3X&lt;/a&gt;&amp;rdquo; model as a framework to hold the talk together.&lt;/p&gt;
&lt;p&gt;As well as being part of the show at SeConf, I also had the pleasure of helping out Jim Evans in the &amp;ldquo;Fix a Bug, Become a Committer&amp;rdquo; workshop. He did a great job explaining how the pieces fit together, and by the end of the workshop, we had everyone building Selenium and running tests in their IDEs of choice (provided that choice wasn&amp;rsquo;t &amp;ldquo;Eclipse&amp;rdquo;), which is a testament to the hard work he&amp;rsquo;d put into preparing the session. It did highlight that the &amp;ldquo;getting started&amp;rdquo; docs probably need a bit of a polish to become usable. I was also invited to do a Q&amp;amp;A with the folks in the &amp;ldquo;Selenium Grid&amp;rdquo; workshop, where I broke from theme to talk about the role of QA in a team. Thanks for being patient, everyone!&lt;/p&gt;
&lt;p&gt;In terms of code, as I write this, I&amp;rsquo;ve landed 57 commits since September 17th. Part of this was to help shape the 3.6 release. For Java, the theme of this release was the slow deprecation of the amorphous blob of data that is &amp;ldquo;DesiredCapabilities&amp;rdquo; to the more strongly-typed &amp;ldquo;*Options&amp;rdquo; classes (eg. FirefoxOptions, ChromeOptions, etc). The idea behind the original WebDriver APIs was to lead people in the right direction: if they could hit the &amp;ldquo;autocomplete&amp;rdquo; keyboard combination in their IDE of choice, then they&amp;rsquo;d be able to figure out what to do next. The strong typing is a continuation of this concept, and is something that all the main contributors are fans of.&lt;/p&gt;
&lt;p&gt;One implementation detail we made in the Java tree is that each of the Options classes are also Capabilities. I made this choice for two reasons. The first is philosophical. We don&amp;rsquo;t know ahead of time what new features will land in browsers (headless running for Chrome and Firefox are examples), so we&amp;rsquo;ll always need an &amp;ldquo;escape hatch&amp;rdquo;, to allow people to set additional settings and capabilities we&amp;rsquo;re not aware of. The second is pragmatic. The internals of Selenium&amp;rsquo;s java code is set up to deal with Capabilities, and people extending the framework have been dealing with them as an implicit contract of the code.&lt;/p&gt;
&lt;p&gt;In the wild, there are two major, and one very minor, &amp;ldquo;dialects&amp;rdquo; of the JSON-based protocol spoken by the various implementations. The first is the original &amp;ldquo;JSON Wire Protocol&amp;rdquo;, and the second is the version of that protocol that has been standardised as part of the W3C &amp;ldquo;WebDriver&amp;rdquo; specification. We took pains when standardising to make sure that a JSON Wire Protocol response is almost always a valid W3C response (technical note: because all values are returned as a JSON Object with a &amp;ldquo;value&amp;rdquo; entry, which contains the return value), but there are two areas where the dialects diverge wildly.&lt;/p&gt;
&lt;p&gt;One area is around the &amp;ldquo;&lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#actions&#34;&gt;Advanced User Interactions&lt;/a&gt;&amp;rdquo; APIs. The end point offered by the W3C spec is significantly more flexible and nifty than the original version in the Selenium project, but it is also a lot more complex to implement.&lt;/p&gt;
&lt;p&gt;The other area is around &amp;ldquo;&lt;a href=&#34;https://w3c.github.io/webdriver/webdriver-spec.html#new-session&#34;&gt;New Session&lt;/a&gt;&amp;rdquo;, which is command used to create a new Selenium session. The JSON Wire Protocol demands that the user place the set of features that they&amp;rsquo;re interested in using into a &amp;ldquo;desiredCapabilities&amp;rdquo; JSON blob. This was originally designed as part of a &amp;ldquo;resource acquisition is initialisation&amp;rdquo; pattern &amp;mdash; you&amp;rsquo;d load up the blob with everything you might want (a chrome profile, an equivalent firefox profile, the proxy you&amp;rsquo;d like to use) mashing together items that theoretically only belonged to one browser into a single unit. The remote end was then to do a &amp;ldquo;best effort&amp;rdquo; attempt to meet those requirements, and then report back what it had provided. The local end (the driver code) was then to test whether or not the returned driver was suitable for whatever it was that users wanted to do. Which is why they were called &lt;em&gt;desired&lt;/em&gt; capabilities &amp;mdash; you made a wish, and then could look to see if it came true. If nothing matched, it was legit for a selenium implementation to just start up any driver and give you that.&lt;/p&gt;
&lt;p&gt;The W3C protocol is a lot more structured. It provides for an ordered series of matches that can be made, with capabilities that must be present in all cases. For our example above, the proxy would be used for any driver, and then there&amp;rsquo;d be an ordered set of possible matches for chrome and then firefox (or vice versa). Each driver provider gets a chance to fulfill that request, and if it can, then we use that driver. If nothing matches, then we fail to initialise the session and return an exception to the users.&lt;/p&gt;
&lt;p&gt;The more structured data used by the W3C New Session command is sent in a different key in the JSON blob, and this is by design. In theory, it&amp;rsquo;s possible to map a JSON Wire Protocol &amp;ldquo;New Session&amp;rdquo; payload to the W3C one, and to map the W3C structure to something close to the JSON Wire Protocol payload. Sadly, this process is complex and error prone, and there are language bindings that have been released that get this wrong to one degree or another (and, indeed, some that don&amp;rsquo;t even make the effort) All this means that the Selenium Server has to try and discern the user&amp;rsquo;s intent from the blob of data sent across the wire. Getting this right, and flexible, has been the focus of the forthcoming 3.7 release.  It&amp;rsquo;s fiddly work, but it&amp;rsquo;ll be worth it in the end.&lt;/p&gt;
&lt;p&gt;Another common problem we see is that some servers out there speak the W3C protocol natively (eg. IEDriverServer, geckodriver, the Selenium Server) and others don&amp;rsquo;t yet (eg. safaridriver, chromedriver, and services such as Sauce Labs). A big part of the 3.5 release was the &amp;ldquo;pass through&amp;rdquo; mode, which means that if the Selenium Server detects that both ends speak the same &amp;ldquo;dialect&amp;rdquo; of the wire protocol, it&amp;rsquo;ll just shuttle data backwards and forwards. However, if it detects that the two ends don&amp;rsquo;t speak the same protocol, it&amp;rsquo;ll do &amp;ldquo;protocol conversion&amp;rdquo;, mapping JSON Wire Protocol calls to and from W3C ones. This has been made easier by the fact that the W3C spec is congruent with the JSON Wire Protocol &amp;ndash; the two have identical end points for many commands.&lt;/p&gt;
&lt;p&gt;But not all commands. The main ones that have been causing grief have been the advanced user interaction commands, particularly when a local end speaks the JSON Wire Protocol, and the remote end speaks the W3C one. Just such this situation arises for users of some cloud-based Selenium servers, and its been a constant source of questions from users. To help address this, I&amp;rsquo;ve landed some code that does emulation of the common JSON Wire Protocol advanced user interaction commands (things like &amp;ldquo;moveTo&amp;rdquo;). Hopefully this will address the majority of headaches that people are experiencing using this new functionality.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see what the next month brings. Hopefully, we&amp;rsquo;ll ship 3.7 :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Poetry of Code</title>
      <link>https://www.rocketpoweredjetpants.com/2016/06/the-poetry-of-code/</link>
      <pubDate>Fri, 03 Jun 2016 09:37:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2016/06/the-poetry-of-code/</guid>
      <description>&lt;p&gt;Write a poem about a sunrise. Perhaps you&amp;rsquo;ll leap straight in, and start writing freeform verse. Perhaps you&amp;rsquo;ll choose a style; a haiku, or a limerick? Something using iambic pentameter or rhyming couplets? Your choice of approach places tangible constraints on how you express yourself.&lt;/p&gt;
&lt;p&gt;What aspect of the sunrise will you write about? The sun itself, or the environment it rises over? Maybe there&amp;rsquo;s a seascape to be evoked, or mountains. Maybe a city?&lt;/p&gt;
&lt;p&gt;Now ask a friend to write a poem about a sunrise. I promise you, it won&amp;rsquo;t be the same. To the outside observer watching you work, both of you will look alike &amp;mdash; scratching words on a page with a pen &amp;mdash; but the results are wildly different.&lt;/p&gt;
&lt;p&gt;You both work alone. Your art is your own. It&amp;rsquo;s wonderful.&lt;/p&gt;
&lt;p&gt;Write a program to sort some numbers. Perhaps you&amp;rsquo;ll leap straight in, and start writing freeform code. Perhaps you&amp;rsquo;ll choose a style; Object Orientation perhaps, or a functional approach? Something using Java or Python? Your choice of approach places tangible constraints on how you express yourself.&lt;/p&gt;
&lt;p&gt;What algorithm will you choose to write? A bubble sort, or a quick sort? Maybe a shell sort to be implemented, or a sleep sort? Maybe there&amp;rsquo;s some other approach?&lt;/p&gt;
&lt;p&gt;Now ask a friend to write a program to sort some numbers. I promise you, it won&amp;rsquo;t be the same. To the outside observer watching you work, both you will look alike &amp;mdash; typing words on a keyboard &amp;mdash; but the results are wildly different.&lt;/p&gt;
&lt;p&gt;You seldom work alone. Your art is a collaborative exercise. It&amp;rsquo;s wonderful.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sometimes I&#39;m an Idiot</title>
      <link>https://www.rocketpoweredjetpants.com/2015/12/sometimes-im-an-idiot/</link>
      <pubDate>Fri, 04 Dec 2015 15:00:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2015/12/sometimes-im-an-idiot/</guid>
      <description>&lt;p&gt;Recently, I&amp;rsquo;ve had a few too many things on my plate to deal with, and have been flirting with burn out, so it&amp;rsquo;s time to take stock and stop being an idiot. In order to stop doing something, one must understand the things that are being done. In light of that, let me enumerate some of the ways in which I am an idiot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recently, for only the second time this year, I went to one of the many social events organised by work. They&amp;rsquo;re a great chance to hang out with people I&amp;rsquo;d not normally see. I&amp;rsquo;ve been an idiot for prioritizing sitting at a keyboard over spending time getting to know other people and getting a better view of the company I work for. More broadly, I spend too much time at work, and it doesn&amp;rsquo;t bring happiness. Don’t you be an idiot too. Go and talk to someone.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve still got about half my annual leave to take even though it’s now December. I&amp;rsquo;ve been an idiot for not prioritizing resting and looking after myself. I&amp;rsquo;m in touching distance of finishing a big project, and once that&amp;rsquo;s done I&amp;rsquo;ll be taking all my remaining holiday.  You should also take your leave. No project has failed because someone went on holiday. &lt;/li&gt;
&lt;li&gt;For months, I was &amp;ldquo;too busy at work&amp;rdquo; to go to the doctor about a nagging pain in my foot. After it became so chronic walking to the office every morning was painful, I finally caved and went to seek help. It&amp;rsquo;s going to take months to sort this shit out. If I&amp;rsquo;d taken the time to go to the doctor sooner I&amp;rsquo;d be better already, and things wouldn&amp;rsquo;t be as painful or complicated as they are. I was an idiot for not prioritizing my own health. How can I work when I&amp;rsquo;m sick? If you&amp;rsquo;re feeling rubbish, or you need treatment, go and get it. Then, once you’ve done that, come back and be busy.&lt;/li&gt;
&lt;li&gt;Switching off from work is a must. Drawing a line between the office and home is vital. I&amp;rsquo;ve been an idiot for looking at work stuff after hours, when I can&amp;rsquo;t really do anything about it, and never properly disconnecting. Facebook have an app called @Work and a recently-launched @Work Chat app, and I use these extensively. Your work may use a different email or calendar server you use for your personal life. When someone who&amp;rsquo;s not an idiot leaves the offices, they mute work-related conversations, calendars and emails. Not doing that is a great way to burn out. Don&amp;rsquo;t be an idiot.&lt;/li&gt;
&lt;li&gt;At both Google and Facebook, I&amp;rsquo;ve had regular international travel in order to talk to people and collaborate with teams. This has meant I&amp;rsquo;ve not felt able to book myself into after-work courses even though I&amp;rsquo;ve wanted to. I am an idiot for letting work stop me from improving myself and my life. As a concrete example, my girlfriend was Turkish (she’s still Turkish. Figure the rest out for yourself). I thought it would be nice to learn the language so that we could go on holiday, visit her family, or just chat at home (and &amp;mdash; hey! &amp;mdash; learning a new language is always fun). I tried a combination of Rosetta Stone and text books, but I always let work get on top of me, so I never put in the work required. I knew I needed to take a structured class, and I knew that would need to be in the evening. I never signed up, because I was “too busy”. Now I deeply regret that, and kick myself routinely for being an idiot. Yesterday, I finally signed up for a Turkish class even though it’s a ten week course, and even though we’re no longer going out. At least I can still enjoy learning. I really am an idiot for not doing this already.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main lesson I’m (finally!) learning is that I’ve been an idiot because I’ve let work dominate my life. Neither Facebook nor Google made me work these hours, or worry this much, or stress about all the things that I do and have done. I let work do that to me. I’ve had enough of being an idiot.&lt;/p&gt;
&lt;p&gt;I have a horrible feeling that once I’m well rested, de-stressed, and feel like there’s more to life than a constant grind of work, I’ll be better at my job, and happier too. I&amp;rsquo;ll have no way of dealing with not feeling like shit, but it&amp;rsquo;ll be fun to find out. I don’t know whether this will be the case for sure, but doing the same-old, same-old isn’t working well.&lt;/p&gt;
&lt;p&gt;I’ll keep you posted.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Monorepo --- One Source Code Repository to Rule Them All</title>
      <link>https://www.rocketpoweredjetpants.com/2015/04/monorepo---one-source-code-repository-to-rule-them-all/</link>
      <pubDate>Tue, 21 Apr 2015 12:11:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2015/04/monorepo---one-source-code-repository-to-rule-them-all/</guid>
      <description>&lt;p&gt;What is a monorepo? It&amp;rsquo;s a unified source code repository used by an organisation to host as much of its code as possible.&lt;/p&gt;
&lt;p&gt;This is the pattern followed by companies such as Google, Facebook and the BBC, and is the way that I prefer to structure large scale code, as discussed in my post &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2012/11/ruminations-on-code-bases-i-have-known.html&#34;&gt;ruminating on codebases I have known&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not entirely sure where I first heard the term but I like the way it demonstrates an intentional approach, rather than being the result of happenstance, so I use it when I can.&lt;/p&gt;
&lt;p&gt;Of course, you need some tooling around a monorepo of any significant size. At some point, there should be more posts about that, but for now, take a look at the video of how &lt;a href=&#34;https://www.youtube.com/watch?v=X0VH78ye4yY&#34;&gt;Facebook handles this&lt;/a&gt; from F8 in 2015.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Android: Forking Java by Mistake</title>
      <link>https://www.rocketpoweredjetpants.com/2015/02/android-forking-java-by-mistake/</link>
      <pubDate>Thu, 26 Feb 2015 21:46:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2015/02/android-forking-java-by-mistake/</guid>
      <description>&lt;p&gt;Java has been forked, and Google is the reason. Allow me to explain.&lt;/p&gt;
&lt;p&gt;Back in the days of Cupcake and Donut, when Android was new and shiny, one of the things that made it attractive to developers was that they could use a language they were familiar with on this new platform. That language was Java, and of course the version used was a modern one. The most recent version of Java at the time was version 6.&lt;/p&gt;
&lt;p&gt;Of course, Java moved forward. 6 was end-of-lifed in February, 2013, and version 7 is now the oldest version supported by Oracle. Java 7&amp;rsquo;s end of public updates is looming, coming as it does in &lt;a href=&#34;http://www.oracle.com/technetwork/java/eol-135779.html&#34;&gt;April 2015&lt;/a&gt;. Java 7 introduced a bunch of new APIs and useful features. Some of these, such as better generics inference, are syntactic sugar and provided by the compiler, but some of these (notably &amp;ldquo;&lt;a href=&#34;http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html&#34;&gt;try-with-resources&lt;/a&gt;&amp;rdquo;) need support from the runtime. That support only appeared in &lt;a href=&#34;http://www.android.com/versions/kit-kat-4-4/&#34;&gt;Android KitKat&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Versions of Android before KitKat still account for just under 60% of the market according to &lt;a href=&#34;https://developer.android.com/about/dashboards/index.html&#34;&gt;Google&amp;rsquo;s own dashboards&lt;/a&gt;. That means that someone who wants to target as much of the Android market as possible has a choice to make: use Java 7&amp;rsquo;s fancy new features, or stick with Java 6. Android&amp;rsquo;s toolchain supports taking Java 7 bytecode, so all the syntactic sugar provided by the compiler is available, but you can&amp;rsquo;t use the new features. Things are only going to get worse as Java 8 gets wider adoption &amp;mdash; features such as lambdas look like they&amp;rsquo;re going to be widely used, especially as the functional paradigm becomes more widespread.&lt;/p&gt;
&lt;p&gt;Java has a vast collection of OSS and commercial libraries out there for Doing Useful Things. If an app chooses to target Java 6, every library it depends on must also make that same choice.&lt;/p&gt;
&lt;p&gt;This means that the Java market is now forking. Server-side Java is forging ahead, and the libraries that it uses are increasingly starting to use modern Java features, unless enough of their users ask for Android compatibility.&lt;/p&gt;
&lt;p&gt;So, what can Google do to keep the world moving forward? How can developers use a more modern Java whilst still being usable by the largest part of the Android market.&lt;/p&gt;
&lt;p&gt;The simplest thing would be to release a shim that developers can optionally load on pre-KitKat Android. Oracle&amp;rsquo;s lawsuit about API infringement may make this a deeply undesirable route for them to follow.&lt;/p&gt;
&lt;p&gt;Alternatively, individual developers can pack any required classes and APIs into their own apps. That seems like an error-prone way of doing things &amp;mdash; it&amp;rsquo;s way too easy to accidentally use these new APIs by accident, and someone who only tests on a recent device will miss the versioning problem.&lt;/p&gt;
&lt;p&gt;Finally, I guess some benevolent third party could create the required shim, but getting widespread usage may be difficult, and it&amp;rsquo;s hardly ideal.&lt;/p&gt;
&lt;p&gt;Once Java 8 features come into widespread use (or the use of &lt;a href=&#34;http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html#invokedynamic&#34;&gt;invokedynamic&lt;/a&gt; gets more traction), the situation won&amp;rsquo;t be so simple. I seriously hope the big brains running Android are getting ahead of this problem &amp;mdash; we&amp;rsquo;ll need platform support to solve this problem properly, and we&amp;rsquo;ll need it soon.&lt;/p&gt;
&lt;p&gt;Until that support arrives, Java has been forked, with two family trees each with Java 6 as their common ancestor.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Pioneers and Settlers</title>
      <link>https://www.rocketpoweredjetpants.com/2014/10/pioneers-and-settlers/</link>
      <pubDate>Wed, 01 Oct 2014 13:27:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2014/10/pioneers-and-settlers/</guid>
      <description>&lt;p&gt;I enjoyed breaking the world up into two kinds of developers so much, I thought I&amp;rsquo;d do it again.&lt;/p&gt;
&lt;p&gt;The problems that we ask developers to solve are many and varied, but they all contain some mixture of the known and the unknown (sorry to come over all Rumsfeld this early in the post). A new project, the kind of which the company has never undertaken before, is riddled with the unknown to start with, whereas rewriting the legacy system in another language with a team who know the system and both the old and new languages is a pretty solid slab of known nastiness.&lt;/p&gt;
&lt;p&gt;The kind of person you need for each type of problem differs. I used to think of them as &amp;ldquo;Starters&amp;rdquo; and &amp;ldquo;Finishers&amp;rdquo;, but those terms are anodyne and lack the opportunity to be grossly misinterpreted. I call them &amp;ldquo;Pioneers&amp;rdquo; and &amp;ldquo;Settlers&amp;rdquo; now.&lt;/p&gt;
&lt;p&gt;A pioneer is the kind of person you want to tackle a problem rife with the unknown. They&amp;rsquo;re undaunted by the lack of a map, and positively enjoy the uncertainty. They tend to operate on their own, or in very small groups, and explore a problem with gusto. There&amp;rsquo;s a strong chance code will be written, discarded, and written again, many times over. They might stand at a whiteboard and argue about design, covering it with boxes, arrows and misleading labels, before deciding the best thing to do is to build both approaches and see which one works.&lt;/p&gt;
&lt;p&gt;But they get the job done. And once they know that the unknowns have been worked on, reduced to a manageable level and understood, they lose interest. The thing that drives them is taking a challenge that no-one else has overcome and showing that it&amp;rsquo;s not really _that_ hard.&lt;/p&gt;
&lt;p&gt;A pioneer is just the kind of person you want to get a project off the ground. And then you probably want them off the team. They&amp;rsquo;ve served their purpose, and now they&amp;rsquo;re going to look for trouble.&lt;/p&gt;
&lt;p&gt;What you want after the original skeleton is in place are Settlers. They take the rough trial laid out, and they make it habitable, maintainable and a Nice Place To Be. The problems that they solve tend not to be the &amp;ldquo;what the heck are we trying to do?&amp;rdquo; ones, but the &amp;ldquo;how the heck are we going to make this work?&amp;rdquo; ones. They&amp;rsquo;re qualitatively different types of question, with very different challenges.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s entirely possible that the pioneer, hotshot iconoclast that they were, has blazed a trial through the least pleasant route. They were only interested in getting things working, not doing it the best way possible. The settlers may be forced to throw out everything that the pioneer has done, leaving just the faint whiff of the original scheme in the air. I suspect that they do this more than most teams would like to admit.&lt;/p&gt;
&lt;p&gt;It is vital to note that the challenges and design question a settler faces are no less taxing than those faced by pioneers, they&amp;rsquo;re just different.&lt;/p&gt;
&lt;p&gt;The pioneer finds &amp;ldquo;what&amp;rdquo; satisfying, and the process of solving &amp;ldquo;how&amp;rdquo; an anathema once there&amp;rsquo;s working code. A settler finds iterating on the &amp;ldquo;how&amp;rdquo; a deeply rewarding experience, but the &amp;ldquo;what&amp;rdquo; may not hold much interest at all.&lt;/p&gt;
&lt;p&gt;Of course, it depends on the project to determine what the correct mix of pioneers to settlers is. Sometimes, you just need a small team of pioneers. Sometimes you need a room full of settlers. Sometimes you need to start with pioneers, and then replace them with settlers. It&amp;rsquo;s okay. The types of problems that need to be solved on a project vary over time. If they didn&amp;rsquo;t, software development wouldn&amp;rsquo;t be this much fun.&lt;/p&gt;
&lt;p&gt;If you ever work with me, the chances are that you&amp;rsquo;ll find I like the pioneer work an awful lot. I like to go shooting off into the darkness, meandering with glee into rough edges and emerging, triumphant and bleeding into the light having shown that, yes, yes it is possible to do something crazy. It takes a force of will for me to be a settler, and I rapidly get uneasy and unhappy when I try it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cavemen and Plainsmen</title>
      <link>https://www.rocketpoweredjetpants.com/2014/09/cavemen-and-plainsmen/</link>
      <pubDate>Fri, 26 Sep 2014 15:37:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2014/09/cavemen-and-plainsmen/</guid>
      <description>&lt;p&gt;It is, of course, a gross overgeneralisation to say that there are two kinds of developers in the world. In this post I plan to grossly overgeneralise by discussing the two kinds of developers that are out there in the world.&lt;/p&gt;
&lt;p&gt;Software developers like to write code, but the way that a developer likes to write code can vary wildly. Take, for example, the caveman. When given a problem, the caveman likes to retreat into his cave. There, in the dark, and away from the prying eyes of the people around him, they can take the rough stone of the basic problem and fashion it into a thing of beauty. The art of creation take a long time, so for those of us watching from the outside, it&amp;rsquo;s as if the developer has ceased to exist.&lt;/p&gt;
&lt;p&gt;Finally, the caveman emerges from the gloom, clutching the gorgeous artifact you&amp;rsquo;ve been waiting to see for so long. Or rather, they sometimes emerge from the gloom clutching a precious artifact. Sometimes, in the dark, they drop the rough stone they&amp;rsquo;re working on and accidentally pick up a coprolite. This fossilised turd has now been thoroughly polished and shaped. It&amp;rsquo;s perfect, but there&amp;rsquo;s no mistaking that it&amp;rsquo;s a turd.&lt;/p&gt;
&lt;p&gt;Oh well. At least while it was happening the manager could relax and rest easy: although they had no idea what their caveman was working on, it was clear that they working on something because they were in the cave.&lt;/p&gt;
&lt;p&gt;Contrast this with the plainsman.&lt;/p&gt;
&lt;p&gt;Give a plainsman a problem, and they will gleefully leap about, showing it to everyone and anyone. It&amp;rsquo;s amazing to watch one of these in action. Ideas fizz about them, and new approaches to tackle the problem are tried and discarded. You&amp;rsquo;ll know that a plainsman is working on a problem because you&amp;rsquo;ll see it. They might be vocal, they might be chatty on groups. Who knows? But you&amp;rsquo;ll see and feel the heat of creation.&lt;/p&gt;
&lt;p&gt;Eventually, the plainsman will come off the savannah and show you the end product. It may be the glorious artifact you hoped for, or, as with the caveman, they may have become distracted and hand over some sort of steampunk turd.&lt;/p&gt;
&lt;p&gt;Of course, in the process of doing this, they may well have given their manager a few scares and worrisome moments &amp;mdash; progress may have appeared slow, or a deadend may have been investigated for too long. Their manager may well be ever-so-slightly balder than before. Stress does that.&lt;/p&gt;
&lt;p&gt;Taking a step back, and only looking at the starting point and the end point, the two kinds of developer are identical. They&amp;rsquo;re both given a problem, and they both sometimes solve it, and sometimes they royally screw it up. It turns out that they both probably follow the same techniques and processes to figure out how to build the software they&amp;rsquo;re crafting. It&amp;rsquo;s just that the caveman keeps this quiet, and doesn&amp;rsquo;t like people to know how things are going until they&amp;rsquo;re done, whereas a plainsman has never managed to figure out how to turn off the noise, or has consciously dialed up the volume.&lt;/p&gt;
&lt;p&gt;Managers may actually prefer a caveman. If the end result is going to be the same anyway, it&amp;rsquo;d be nice to have a quiet life, free of stress, so that the manager can get on with the important work of whatever it is that managers do (Gantt Charts? Going to meetings? Browsing the web? Who knows &amp;mdash; they&amp;rsquo;re a mystery to me, much like cats)&lt;/p&gt;
&lt;p&gt;The manager is wrong, of course. It&amp;rsquo;s infinitely preferable to work with a plainsman.&lt;/p&gt;
&lt;p&gt;The reason is that it&amp;rsquo;s a rare developer who has to work entirely alone and isolated. They tend to work in teams (as an aside, what is the collective noun for developers? A confusion? A multi-faceted-opinion?) Within that team, there&amp;rsquo;s normally someone who needs the code that our hypothetical developer is working on. Being able to see progress allows others to prioritise their own work. And that moment, where the idea is dropped, and the turd picked up? That moment may not go unobserved in a group.&lt;/p&gt;
&lt;p&gt;Managers know this too. I poke fun at them because I can. Not cats, though. Never poke fun at a cat.&lt;/p&gt;
&lt;p&gt;Now, although I present this in black-and-white terms, it should go without saying (though I&amp;rsquo;ll say it anyway) that it&amp;rsquo;s a rare developer indeed who sits at one or other of these extremes. You can spot a caveman by the feedback from their peers. Things like &amp;ldquo;needs to work on communication&amp;rdquo;, or &amp;ldquo;where the did this highly polished turd come from?&amp;rdquo; A plainsman might have feedback saying that they&amp;rsquo;re noisy.&lt;/p&gt;
&lt;p&gt;If you ever work with me, I&amp;rsquo;m a plainsman if you&amp;rsquo;re within earshot. Ask anyone who&amp;rsquo;s worked with me, that&amp;rsquo;s quite a distance. However, if you&amp;rsquo;re not on the IRC channel I&amp;rsquo;m on and out of earshot, I&amp;rsquo;m a pretty effective caveman. Which means that I should never be left on my own. Or fed after midnight. No. Hang on. That&amp;rsquo;s Gremlins, right?&lt;/p&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;m not trapped in here with them. They&amp;rsquo;re trapped in here with me.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I really like the terms &amp;ldquo;caveman&amp;rdquo; and &amp;ldquo;plainsman&amp;rdquo;, mainly because I find that they&amp;rsquo;re ones that people remember easily, and which fit the premise of the analogy well, but I&amp;rsquo;m aware that they&amp;rsquo;re not gender-neutral. Suggestions for replacements have been &amp;ldquo;morlocks&amp;rdquo; and &amp;ldquo;eloi&amp;rdquo;, or &amp;ldquo;troglodytes&amp;rdquo; and &amp;ldquo;herders&amp;rdquo;, but both of those cast the caveman in a pretty negative light, which isn&amp;rsquo;t really what I want to say. &amp;ldquo;Cavedweller&amp;rdquo; and &amp;ldquo;plains-dweller&amp;rdquo; are probably the best alternatives.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Ranty and Dogmatic Troll Masquerading as Coding Guidelines</title>
      <link>https://www.rocketpoweredjetpants.com/2014/01/a-ranty-and-dogmatic-troll-masquerading-as-coding-guidelines/</link>
      <pubDate>Fri, 24 Jan 2014 13:55:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2014/01/a-ranty-and-dogmatic-troll-masquerading-as-coding-guidelines/</guid>
      <description>&lt;p&gt;This document represents a series of guidelines for writing code that will swiftly pass code review with my team. It doesn&amp;rsquo;t attempt to be fair. It doesn&amp;rsquo;t attempt to listen to your opinion. It does present a series of guidelines. You may chose to ignore those guidelines (after all, if they had to be obeyed, we&amp;rsquo;d have called them &amp;ldquo;laws&amp;rdquo;) We may choose to point back to these when reviewing your code. We&amp;rsquo;ll attempt to avoid being (passive) aggressive when we do so.&lt;/p&gt;
&lt;p&gt;Hugs and cuddles.&lt;/p&gt;
&lt;h3 id=&#34;test-first&#34;&gt;Test first&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re working on a framework for writing automated tests. It&amp;rsquo;s probably a good idea for us to lead by example and write some tests. It hasn&amp;rsquo;t been proven — but it&amp;rsquo;s a scientific fact — that writing tests after the fact is as boring as boring can be. So we write the tests first. This has the added advantage that we can think about the kind of functionality we want independent of how it&amp;rsquo;s implemented.&lt;/p&gt;
&lt;h3 id=&#34;kiss-yagni&#34;&gt;KISS, YAGNI&lt;/h3&gt;
&lt;p&gt;Or &amp;ldquo;Keep It Stupidly Simple, You Ain&amp;rsquo;t Gonna Need It&amp;rdquo;. When writing new code, just write the code you need. Don&amp;rsquo;t attempt to write some über-generic, ultra-lightweight, whizz-bang sub-framework for handling every conceivable edge case when you only need to do &amp;ldquo;this thing&amp;rdquo; once. It&amp;rsquo;s another Scientific Fact that engineers are bloody awful at spotting patterns before the patterns emerge. Wait until the third or even fourth time you repeat something before attempting to extract a common API.&lt;/p&gt;
&lt;p&gt;A good book for this? &amp;ldquo;&lt;a href=&#34;http://www.amazon.co.uk/Refactoring-Patterns-Addison-Wesley-Signature-Kerievsky/dp/0321213351/ref=sr_1_1?ie=UTF8&amp;amp;qid=1385652714&amp;amp;sr=8-1&amp;amp;keywords=refactoring+to+patterns&#34;&gt;Refactoring to Patterns&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;h3 id=&#34;prefer-composition-over-inheritance&#34;&gt;Prefer composition over inheritance&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s admit this up front: Java&amp;rsquo;s a deeply flawed language. If I had to write this crap in vi or emacs, I&amp;rsquo;d be sitting in a corner, rocking backwards and forwards, weeping gently and pulling at my hair. Fortunately, we have IDEs, so It&amp;rsquo;s All Okay. One area where Java is flawed is that it&amp;rsquo;s really easy to inherit from a base class, and really clumsy to do proper composition. Nevertheless, as a rule of thumb: &lt;strong&gt;inheritance of interfaces is cool, subclassing a concrete base class is not.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, there will be times where it&amp;rsquo;d be Really Useful to be able to share some common functionality between things that appear to be related. For some reason, this always seems to happen with base test classes. It&amp;rsquo;s a weird fetish, and it&amp;rsquo;s one we should be disabused of. There are better ways to handle this, possibly through JUnit&amp;rsquo;s Rules, or by extracting the common functionality into its own class and just newing it up on demand.&lt;/p&gt;
&lt;p&gt;Point is: if you&amp;rsquo;re using inheritance to share some common functionality between otherwise unrelated classes (and &amp;ldquo;it&amp;rsquo;s a test&amp;rdquo; doesn&amp;rsquo;t make them related) you&amp;rsquo;re not doing inheritance right. You are, in fact, Doing It Wrong.&lt;/p&gt;
&lt;h3 id=&#34;role-based-interfaces-ftw&#34;&gt;Role-based interfaces #ftw&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;http://martinfowler.com/bliki/RoleInterface.html&#34;&gt;Role-based interfaces&lt;/a&gt;: we like them, we use them, and we encourage other people to use them. Yes, this will mean that your classes might implement a lot of interfaces. That means that they collaborate with a lot of other classes. That tells you something. It probably tells you your class has too many roles or responsibilities.&lt;/p&gt;
&lt;p&gt;Remember, kids, inside every fat class are at least two classes waiting to climb out.&lt;/p&gt;
&lt;h3 id=&#34;expose-collaborators&#34;&gt;Expose Collaborators&lt;/h3&gt;
&lt;p&gt;We take the use of Dependency Injection as an article of faith. This does not mean that we whole-heartedly embrace the need for a &amp;ldquo;DI Container&amp;rdquo;, but it does mean that we&amp;rsquo;re huge fans of exposing the collaborators of a class via its constructor (because &amp;ldquo;constructor-based DI&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;Having said that, &amp;ldquo;new&amp;rdquo; is not a dirty word. It&amp;rsquo;s totally pukka to use it, particularly for those handy utility classes we mentioned above. Having said that….&lt;/p&gt;
&lt;h3 id=&#34;utility-classes-just-say-no&#34;&gt;Utility Classes: Just Say No&lt;/h3&gt;
&lt;p&gt;A utility class represents a severe failure of imagination. They tend to become dumping grounds for only loosely related methods, which are typically static. If you have one of these, a fun exercise is to decompose it into what I like to refer to as &amp;ldquo;Objects&amp;rdquo; and use those instead. If you&amp;rsquo;re having trouble with the traditional &amp;ldquo;noun-based&amp;rdquo; approach to identifying classes, try a bit of &lt;a href=&#34;http://codemanship.co.uk/parlezuml/blog/?postid=987&#34;&gt;London School&lt;/a&gt; TDD and see what shakes loose.&lt;/p&gt;
&lt;h3 id=&#34;singletons-static-methods-also-no&#34;&gt;Singletons? Static Methods? Also No&lt;/h3&gt;
&lt;p&gt;Singletons (in the traditional &amp;ldquo;implemented as a static field in a class&amp;rdquo; sense, not in the &amp;ldquo;ideally we&amp;rsquo;d only have one of these&amp;rdquo; sense) destroy our ability to have fun and write tests that can run in parallel, slashing our potential productivity. Also, it leads people to start using the Service Locator pattern instead of Dependency Injection, and we take DI as an article of faith (see above), mainly because it facilitates TDD by making collaborators clear, like we (also) said above.&lt;/p&gt;
&lt;h3 id=&#34;use-strong-types-where-possible&#34;&gt;Use strong types where possible&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re using a high ceremony language. Might as well embrace that properly. We dislike Stringly-typed code, and we like &lt;a href=&#34;http://darrenhobbs.com/2007/04/11/tiny-types/&#34;&gt;Tiny Types&lt;/a&gt;. Why do we like them? Because they allow our code to express intent as clearly as possible, and we can do things like &amp;ldquo;hang behaviour&amp;rdquo; off them as the need arises.&lt;/p&gt;
&lt;p&gt;BTW, this means that we really should never be returning &amp;ldquo;WebElement&amp;rdquo; from a Page Object. Return a class that models the thing the user would expect to be returned, even if that leads to a class with nothing but a constructor.&lt;/p&gt;
&lt;h3 id=&#34;use-the-most-abstract-type-that-conveys-intent-for-variables-and-fields&#34;&gt;Use the most abstract type that conveys intent for variables and fields&lt;/h3&gt;
&lt;p&gt;The most abstract type explains to the reader what you do. The concrete type is how you&amp;rsquo;re going to do it. You should be able to change your mind about &amp;ldquo;how&amp;rdquo; without needing to change the &amp;ldquo;what&amp;rdquo;. The most abstract type that conveys intent for a variable (&amp;ldquo;Map&amp;rdquo; instead of &amp;ldquo;HashMap&amp;rdquo;, &amp;ldquo;WebDriver&amp;rdquo; instead of &amp;ldquo;DroidDriver&amp;rdquo;)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been asked whether a method should return an ImmutableSet or just a Set when it returns a set of things that both sorted and immutable. Redundant as this question may seem, I&amp;rsquo;ll still have a bash at answering it. Return the ImmutableSet, as that conveys the intent of the return value. If the caller doesn&amp;rsquo;t care about the mutability of the set, they can assign it to a Set. Everyone&amp;rsquo;s happy.&lt;/p&gt;
&lt;h3 id=&#34;use-the-right-naming-convention&#34;&gt;Use the right naming convention&lt;/h3&gt;
&lt;p&gt;The naming convention in our codebase is a hangover from the Android coding style, which was created by people who wrote C++ for a living (which also explains why there are so many static methods in the framework too) We don&amp;rsquo;t write C++ for a living, and using a foreign language&amp;rsquo;s coding conventions in Java code makes you look like a clown and an arsehole.&lt;/p&gt;
&lt;p&gt;However, it appears we&amp;rsquo;re perfectly happy to present ourselves as people who find it hard to get dressed in the morning without hurting ourselves. Consequently, when writing code that&amp;rsquo;s just for us, use the coding standard of the rest of the codebase and choke back the waves of nausea. If you&amp;rsquo;re writing code that we&amp;rsquo;ll put in front of a public who believe we&amp;rsquo;re competent engineers (that is, OSS) use the Google coding standard (effectively Oracle&amp;rsquo;s, but with a two space indent)&lt;/p&gt;
&lt;h3 id=&#34;use-the-ubiquitous-language-luke&#34;&gt;Use the Ubiquitous Language, Luke&lt;/h3&gt;
&lt;p&gt;If everyone calls it a &amp;ldquo;Self Aggrandizsing Wattle&amp;rdquo;, don&amp;rsquo;t name the class &amp;ldquo;AndroidPoweredMultiflexViewPort&amp;rdquo;. We want people to find the classes we write, and we want them to understand how they relate to other classes in the system. Using the names that people call things as class names is Totally Cool.&lt;/p&gt;
&lt;p&gt;Also, if you&amp;rsquo;ve not done so, go and grab a copy of &lt;a href=&#34;http://www.amazon.co.uk/Domain-driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?ie=UTF8&amp;amp;qid=1385652471&amp;amp;sr=8-1&amp;amp;keywords=domain+driven+design&#34;&gt;Domain Driven Design&lt;/a&gt; and attempt to wade through as much of it as you can bear. Then skip to the end and read the bit about Anti-Corruption Layers. That bit&amp;rsquo;s good.&lt;/p&gt;
&lt;h3 id=&#34;naming-things&#34;&gt;Naming Things&lt;/h3&gt;
&lt;p&gt;Design Patterns are a means of communication, not blueprints. Similarly, the thing that makes classes interesting isn&amp;rsquo;t what pattern they happen to implement, it&amp;rsquo;s the role that they play in our system. Leave the pattern name off the class name, ok? The exception to this is the &amp;ldquo;Builder&amp;rdquo; pattern. Everyone expects the &amp;ldquo;Builder of Thing&amp;rdquo; to be called &amp;ldquo;ThingBuilder&amp;rdquo;. We might as well go with the flow on this one and buck our own contrarian ways.&lt;/p&gt;
&lt;p&gt;Similarly, every concrete class is an implementation of something, so using the postfix &amp;ldquo;Impl&amp;rdquo; (presumably if you&amp;rsquo;re too lazy to name something properly, you&amp;rsquo;re too lazy to type &amp;ldquo;Implementation&amp;rdquo;) as a class name is a Dumb Thing To Do. Name the class for the particular thing that makes it interesting within the system, or prefix the name with &amp;ldquo;Default&amp;rdquo; if there is genuinely nothing interesting about it. Try and avoid naming the class around some obscure implementation detail that no-one using the class cares about.&lt;/p&gt;
&lt;p&gt;BTW, it&amp;rsquo;s acceptable to append the name of the interface being implemented to the class name, but it&amp;rsquo;s better to try and name the class for the role it plays in the system.&lt;/p&gt;
&lt;h3 id=&#34;keep-it-solid&#34;&gt;Keep It SOLID&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Single Responsibility Principle&lt;/li&gt;
&lt;li&gt;Open/Closed Principle&lt;/li&gt;
&lt;li&gt;Liskov Substitution Principle&lt;/li&gt;
&lt;li&gt;Interface Segregation Principle&lt;/li&gt;
&lt;li&gt;Dependency Inversion Principle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Presumably you&amp;rsquo;re a Software Engineer. If any of those are hard to understand, then I&amp;rsquo;d suggest using your favourite search engine to read up on them.&lt;/p&gt;
&lt;h3 id=&#34;document-in-proper-english-that-which-needs-documenting&#34;&gt;Document in Proper English That Which Needs Documenting&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s a safe assumption that anyone actually reading your code is a professional software developer. Telling them stuff that they can see just by reading your code in javadocs and comments is Not Helping, so don&amp;rsquo;t do it. Use comments to explain the reasoning behind particular design decisions, or to alert people to odd corner cases that might actually need explanation.&lt;/p&gt;
&lt;p&gt;Consider replacing one line comments of a block of code with a sensibly named method containing that block of code. After all, when it comes to maintaining this shit, only the most dedicated of developers will actually update the code and the docs.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s cool to use correct grammar and punctuation. Please do so, and try and end sentences with a period cretaceous&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On the Naming of Tests</title>
      <link>https://www.rocketpoweredjetpants.com/2013/11/on-the-naming-of-tests/</link>
      <pubDate>Mon, 04 Nov 2013 13:32:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/11/on-the-naming-of-tests/</guid>
      <description>&lt;p&gt;I&amp;rsquo;d thought that this was part of the automated testing canon already, but apparently not, so a quick note on the naming of tests appears to be in order. Well, how I think tests should be named. :)&lt;/p&gt;
&lt;p&gt;When using an xUnit-style framework, the common pattern is to test class Foo in another class called FooTest. Within this test class, there are several methods. The principle I like to follow is that if you took the name of the test class, stripped off the &amp;ldquo;Test&amp;rdquo; postfix, and then listed the names of the tests as bullet points, you&amp;rsquo;d end up with a list of roles and responsibilities of the class under test. You&amp;rsquo;d end up with something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Foo  
  * Should eat cheese  
  * Should not consider cake as cheese  
  * Should handle null cheese by throwing a SpecificException  
  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And so on.&lt;/p&gt;
&lt;p&gt;Put another way, if someone were to delete the class under test and the bodies of the tests, could they recreate something functionally identical to the class under test using just the test names?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Time Keeps Ticking</title>
      <link>https://www.rocketpoweredjetpants.com/2013/09/time-keeps-ticking/</link>
      <pubDate>Mon, 09 Sep 2013 14:15:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/09/time-keeps-ticking/</guid>
      <description>&lt;p&gt;A note so that I &lt;strong&gt;never forget again&lt;/strong&gt;: the time used by a ZipEntry instance in Java appears to keep ticking.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ZipEntry entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ZipEntry(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; expected &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; System.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentTimeMillis&lt;/span&gt;();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTime&lt;/span&gt;(expected);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Thread.&lt;span style=&#34;color:#a6e22e&#34;&gt;sleep&lt;/span&gt;(3000);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; seen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;getTime&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// This fails  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;assertEquals(expected, seen);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: it turns out that the problem turns out to be that DOS timestamps only store seconds with a precision of 2 seconds. The above could be reduced to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ZipEntry entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ZipEntry(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Note: we set the seconds to an odd number  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; expected &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Calendar.&lt;span style=&#34;color:#a6e22e&#34;&gt;getInstance&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;(2013, SEPTEMBER, 10, 12, 14, 1)  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;getTimeInMillis&lt;/span&gt;();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTime&lt;/span&gt;(expected);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; seen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; entry.&lt;span style=&#34;color:#a6e22e&#34;&gt;getTime&lt;/span&gt;()  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// This fails  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;assertEquals(expected, seen);  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More on &lt;a href=&#34;http://msdn.microsoft.com/en-us/library/ms724247%28v=vs.85%29.aspx&#34;&gt;MSDN&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>On Managing Time and Direct Emails About Selenium</title>
      <link>https://www.rocketpoweredjetpants.com/2013/07/on-managing-time-and-direct-emails-about-selenium/</link>
      <pubDate>Mon, 22 Jul 2013 08:57:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/07/on-managing-time-and-direct-emails-about-selenium/</guid>
      <description>&lt;p&gt;I tend to get a few requests a day asking for help with a problem with &lt;a href=&#34;http://selenium.dev/&#34;&gt;Selenium&lt;/a&gt;. I almost never reply to these. It&amp;rsquo;s not (just!) because I&amp;rsquo;m an evil minded, grumpy so-and-so, but because it&amp;rsquo;s not a great use of anyone&amp;rsquo;s time.&lt;/p&gt;
&lt;p&gt;I am just one person. I have a full time job, and family and friends that I like to spend time with. I work on Selenium as a volunteer, and that means fitting it in where I can. Fortunately, &lt;a href=&#34;http://www.facebook.com/&#34;&gt;work&lt;/a&gt; are understanding about this, and support my role in the project, which means I do far more than if I only had the occasional evening free. Still, it does mean that I prioritise my time spent on the project. This is how I do so:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Writing code. That comes first.&lt;/li&gt;
&lt;li&gt;Spend time on the IRC channel. I often just lurk here, but it&amp;rsquo;s a handy way to talk to the core development team and keep an eye on what&amp;rsquo;s going on.&lt;/li&gt;
&lt;li&gt;Answer emails to the selenium-developers group. This is where we run the project and hold design discussions. It&amp;rsquo;s not a good place to ask for help, unless that help is about implementing Selenium itself.&lt;/li&gt;
&lt;li&gt;I scan the &lt;a href=&#34;http://groups.google.com/group/webdriver&#34;&gt;webdriver&lt;/a&gt; and &lt;a href=&#34;http://groups.google.com/group/selenium-users&#34;&gt;selenium-users&lt;/a&gt; lists, answering questions where I can and provided I have time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, the nice thing with this ordering is that the further down the list you go, the more people there are who are able to help you with your issue. Put another way: asking for help in the user lists means that you&amp;rsquo;re far more likely to get the help you want. It also means that if someone else runs into the same problem, Google can come to their rescue. A private email doesn&amp;rsquo;t have that benefit.&lt;/p&gt;
&lt;p&gt;I know that may be frustrating for you. I know that it seems to make sense to contact prominent people on the project directly. I understand your particular issue is urgent and important to you. I really do, and that&amp;rsquo;s why I don&amp;rsquo;t answer your emails.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Being Part of a Distributed Team</title>
      <link>https://www.rocketpoweredjetpants.com/2013/07/being-part-of-a-distributed-team/</link>
      <pubDate>Fri, 19 Jul 2013 13:07:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/07/being-part-of-a-distributed-team/</guid>
      <description>&lt;p&gt;At work I&amp;rsquo;m part of a distributed team. Two colleagues and I are based in London, and the rest of the team is in Menlo Park, California. It&amp;rsquo;s been reminding me of some lessons that I&amp;rsquo;ve learnt over the years about working as part of a team that&amp;rsquo;s spread across time zones, and I thought it might be nice to share some of them. Without further ado:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Attempt to get code reviewed by the person most familiar with the area but also by someone who&amp;rsquo;s on the same site as you. This suggests that singletons on a site of their own are a suboptimal thing. Which leads to&amp;hellip;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;hellip; be tolerant of clean up diffs. If the local reviewer approves a change in the code review tool the chances are high it&amp;rsquo;ll be landed. The author of the code is responsible for not being a clown: if there are fundamental design decisions that are still unresolved then landing the change, even if the local reviewer is ok with it, isn&amp;rsquo;t a winning move.&lt;br&gt;
Code is a plastic thing and we have source control. We can fix things up. Taking advantage of that is a Good Thing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One useful pattern I&amp;rsquo;ve seen is to check in a failing but ignored test. It a lovely way of moving things forward without jamming the works, and leaves a clear trail of intent.&lt;/p&gt;
&lt;p&gt;Another example of being good at this: the code reviews which get approved with a list of nits and changes to make before landing. I think that shows great trust in your team, and I like it. It goes without saying that if you&amp;rsquo;re using a system like &lt;a href=&#34;https://code.google.com/p/gerrit/&#34;&gt;Gerrit&lt;/a&gt; (**update: **which submits the code when it sees that a diff has been approved), then this &amp;ldquo;ok but please fix&amp;rdquo; approach won&amp;rsquo;t work as well :)&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Time zones are evil but we must live with them. A great habit for the Brits to get into is to have done code reviews for USian teammates by 3pm BST. For those teammates in America, reviewing code by the British first thing when you get to the office in the morning is an extremely helpful thing do. If you&amp;rsquo;re working with colleagues in Australia, India, China or elsewhere on that side of the globe, be aware of when they come into the office and have your code reviews done by then.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The reasoning is clear: it gives everyone as much time as possible to turn code around and get it reviewed again, as this is when the team&amp;rsquo;s hours overlap. That, in turn, helps the team as a whole move fast.&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Bear in mind the hierarchy of communication:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;nothing -&amp;gt; email group -&amp;gt; email -&amp;gt; IM -&amp;gt; VC -&amp;gt; in person&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The further to the right you are, the lower the latency and the higher the bandwidth. The higher the bandwidth, the quicker misunderstandings can be resolved and design choices made.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Corollary 1&lt;/strong&gt;: if a review is dragging on, hop on to VC, Skype or a Google Hangout, or go and chat to people.&lt;/p&gt;
&lt;p&gt;Observation: the further to the left you are, the more asynchronous communication becomes. If time isn&amp;rsquo;t of the essence, then head left.&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;There&amp;rsquo;s nothing like being in the same place. Travel is bad because it means you&amp;rsquo;re away from family, your regular routines and all the things you love about home. Travel is great because you get to go to new places, hang out with the locals and get to see how other offices work. Although it&amp;rsquo;s probably more financially astute to make the smaller part of the team travel to the larger, it&amp;rsquo;s also unfair to expect the travel to always be done by one part of the team. It turns out that a sense of fairness is the thing that will keep the spirits up in the team and keep everything ticking along nicely.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;m sure there&amp;rsquo;s more that needs to be covered. Things like scheduling meetings and alternating times so that people don&amp;rsquo;t always need to stay late or get up stupidly early, or having a useful glossary of ambiguous terms (&amp;ldquo;let&amp;rsquo;s table this discussion&amp;rdquo;) and other issues and hiccups, but I&amp;rsquo;ve been writing for a while now and this is getting long. So I&amp;rsquo;ll stop.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The UNIX Philosophy, WebDriver and HTTP Status Codes</title>
      <link>https://www.rocketpoweredjetpants.com/2013/06/the-unix-philosophy-webdriver-and-http-status-codes/</link>
      <pubDate>Mon, 24 Jun 2013 21:11:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/06/the-unix-philosophy-webdriver-and-http-status-codes/</guid>
      <description>&lt;p&gt;The UNIX philosophy can be described in many ways (and the &lt;a href=&#34;http://en.wikipedia.org/wiki/Unix_philosophy&#34;&gt;Wikipedia page&lt;/a&gt; has plenty), but I&amp;rsquo;ve always admired its practical application in the wealth of shell commands available to a user. Rather than having a single command that Does Everything, the UNIX shell is a place of small commands focused on doing one thing well, yet which are easy to link together.&lt;/p&gt;
&lt;p&gt;For example, I recently needed to compare the contents of two JAR files and remove class files that were duplicated from one of those jars. I ended up generating the list of shared files via:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;comm -12 &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;jar tf first.jar | sort | uniq&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;jar tf second.jar | sort | uniq&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; | grep -v META
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I doubt very much whether the authors of any of those tools thought that this is what I&amp;rsquo;d be doing, yet because the tools are carefully focused and are easy to chain together, this is a trivial thing to do.&lt;/p&gt;
&lt;p&gt;How, you may ask, does this apply to Selenium? And specifically &lt;a href=&#34;https://code.google.com/p/selenium/issues/detail?id=141&#34;&gt;issue 141&lt;/a&gt;? For those of you who can&amp;rsquo;t be bothered to read the incredibly long list of comments on that issue (now at over 100), this is the one about being able to get HTTP status codes from the WebDriver API. The comments are split between those saying that this functionality doesn&amp;rsquo;t belong in the API, and those who (occasionally very vociferously) claim that it does. &lt;/p&gt;
&lt;p&gt;From a philosophical perspective, the WebDriver API is attempting to model a user interacting with their browser. We attempt to limit the APIs we offer to just those that meet this need, only allowing ourselves to extend it to those very clear cases where the browser is the &lt;a href=&#34;http://en.wikipedia.org/wiki/Single_Source_of_Truth&#34;&gt;Source of Truth&lt;/a&gt; about a particular thing (such as with cookies), or where there&amp;rsquo;s no rational way to cleanly offer a facility (such as executing Javascript &amp;mdash; incidentally, something that I spent a lot of time keeping out of the API)&lt;/p&gt;
&lt;p&gt;HTTP status codes don&amp;rsquo;t fall into either category. The browser isn&amp;rsquo;t the the source of truth about these codes, as that&amp;rsquo;s the originating web server. The user may not be aware of them either; a 404 from a .js file? That&amp;rsquo;d most likely go unnoticed. A 500 from even the main page? That may be returned as a 200 by some app servers in certain configurations. &lt;/p&gt;
&lt;p&gt;So that leaves our users out to dry, right? Well, it would if it wasn&amp;rsquo;t for the UNIX Philosophy. You see, it&amp;rsquo;s ridiculously simple to hook up a proxy that will capture this information for the user if you can&amp;rsquo;t obtain the information by instrumenting the server. You can do it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Explain where your proxy lives  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Proxy proxy &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Proxy();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;proxy.&lt;span style=&#34;color:#a6e22e&#34;&gt;setHttpProxy&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;your_proxy:8080&amp;#34;&lt;/span&gt;);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Now tell the webdriver instance about it  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DesiredCapabilities caps &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; DesiredCapabilities();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;caps.&lt;span style=&#34;color:#a6e22e&#34;&gt;setCapability&lt;/span&gt;(CapabilityType.&lt;span style=&#34;color:#a6e22e&#34;&gt;PROXY&lt;/span&gt;, proxy);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WebDriver driver &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; RemoteWebDriver(caps);  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s 5 lines of code in enormously verbose Java. &lt;/p&gt;
&lt;p&gt;Separating the concerns of &amp;ldquo;browser automation&amp;rdquo; from &amp;ldquo;logging network&amp;rdquo; traffic allows the Selenium developers (most of whom are not paid to work on Selenium) to focus on the problem of driving the browser. It means that they&amp;rsquo;re not working on writing their own HTTP proxy, which is a sufficiently taxing tax that there are many projects out there working to write something solid and stable.&lt;/p&gt;
&lt;p&gt;Great options for users looking for a powerful and capable proxy include &lt;a href=&#34;http://fiddler2.com/&#34;&gt;Fiddler&lt;/a&gt; and &lt;a href=&#34;http://www.charlesproxy.com/&#34;&gt;Charles&lt;/a&gt;. Another option is the &lt;a href=&#34;https://github.com/lightbody/browsermob-proxy&#34;&gt;BrowserMob Proxy&lt;/a&gt;, which started as being a fork from the original Selenium RC codebase (Oh! The irony!) but has since matured and grown. This is amazingly simple to integrate with a WebDriver instance, as &lt;a href=&#34;https://github.com/lightbody/browsermob-proxy#using-with-selenium&#34;&gt;shown in their docs&lt;/a&gt;. For brevity, the integration can be done like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ProxyServer server &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ProxyServer(4444);  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;server.&lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;();  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// get the Selenium proxy object  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Proxy proxy &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; server.&lt;span style=&#34;color:#a6e22e&#34;&gt;seleniumProxy&lt;/span&gt;();  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Following the UNIX approach, we make it easy to use a proxy with the WebDriver API. That means that we&amp;rsquo;re not implementing an API for getting HTTP status codes in the Selenium project not only because it&amp;rsquo;s out of scope, but there are already people doing a great job of offering that capability elsewhere.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Why I Care About Automated Testing</title>
      <link>https://www.rocketpoweredjetpants.com/2013/05/why-i-care-about-automated-testing/</link>
      <pubDate>Wed, 22 May 2013 09:43:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/05/why-i-care-about-automated-testing/</guid>
      <description>&lt;p&gt;I was reading a blog the other day that highlighted the fact that &lt;a href=&#34;http://www.codinghorror.com/blog/2007/05/maximizing-the-value-of-your-keystrokes.html&#34;&gt;I&amp;rsquo;ve only got a limited number of keystrokes to use up&lt;/a&gt; in my working life. I can use those keystrokes on anything: email, writing code, futzing around with the git command line, facebook status messages, posts on Plus, anything&amp;hellip;.&lt;/p&gt;
&lt;p&gt;That got me thinking about why I think that testing is an important part of software development: not an afterthought, but something that&amp;rsquo;s as vital as considering API design or how to structure methods. It&amp;rsquo;s because I only have a certain number of keystrokes. I&amp;rsquo;d rather spend those working on new features and moving the bits of the world I care about forward rather than fixing bugs or chasing down regressions.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s undeniable that writing a test and the code itself takes more time. I&amp;rsquo;m having to write more code. I&amp;rsquo;m burning keystrokes. But each of those tests may be helping to prevent regressions, or providing insight into the structure and usage of my code. And that additional insight, and those prevented regressions, mean that cumulatively I have more time to spend hacking on features, and that&amp;rsquo;s what I love to do.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s why I care about automated tests. That&amp;rsquo;s also why I think you should care too.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Speaking Engagements</title>
      <link>https://www.rocketpoweredjetpants.com/2013/03/speaking-engagements/</link>
      <pubDate>Tue, 26 Mar 2013 10:26:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/03/speaking-engagements/</guid>
      <description>&lt;p&gt;One of the things that I love most about working in tech is the chances I get to speak in public and share some of the knowledge I&amp;rsquo;ve somehow managed to accumulate (even, sometimes, about work that I&amp;rsquo;ve actually done myself!)&lt;/p&gt;
&lt;p&gt;For someone who enjoys speaking in public, I find it hard to actually promote the fact that I&amp;rsquo;ll be in places, but some friends have recently asked when and where I&amp;rsquo;ll be, so without further ado here are my next confirmed appearances:&lt;/p&gt;
&lt;p&gt;7th-10th April: &lt;a href=&#34;http://de.droidcon.com/2013/&#34;&gt;DroidCon&lt;/a&gt;, Berlin&lt;br&gt;
23rd-24th April: &lt;a href=&#34;https://developers.google.com/google-test-automation-conference/&#34;&gt;GTAC&lt;/a&gt;, NY&lt;br&gt;
2nd May: &lt;a href=&#34;https://developers.facebook.com/events/mobiledevcon/london/&#34;&gt;Facebook Mobile Developer Conference&lt;/a&gt; (a brief stint in London!)&lt;br&gt;
10th-12th June: &lt;a href=&#34;http://www.seleniumconf.org/&#34;&gt;Selenium Conf&lt;/a&gt; (&lt;a href=&#34;http://www.seleniumconf.org/tickets/&#34;&gt;tickets on sale&lt;/a&gt;!) (It looks like it&amp;rsquo;ll be great fun!)&lt;/p&gt;
&lt;p&gt;For DroidCon, GTAC and the Facebook MobDevConf, I&amp;rsquo;ll be talking about various aspects of my day job at &lt;a href=&#34;https://www.facebook.com/careers/locations/london&#34;&gt;Facebook&lt;/a&gt;, though each conference, because of their different focuses, will get to hear about different parts of it!&lt;/p&gt;
&lt;p&gt;Selenium Conf is something that I always look forward to. It&amp;rsquo;s a great chance to meet the people who are using the tool that has meant so much to my professional career, and it&amp;rsquo;s also a fantastic opportunity to meet up with the rest of the selenium developer team and buy them a steak dinner. We&amp;rsquo;ve yet to have a veggie join us as a core team member, but we&amp;rsquo;ll figure out a suitable way to reward their efforts too one day! If you&amp;rsquo;re interested in becoming that first vegetarian contributor, then you can always start by &lt;a href=&#34;https://code.google.com/p/selenium/wiki/CommitYourCode&#34;&gt;contributing some code&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also recently become Facebook&amp;rsquo;s W3C AC representative, and will be attending the &lt;a href=&#34;http://www.w3.org/participate/eventscal.html&#34;&gt;TPAC&lt;/a&gt; in November in China.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Clue</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/clue/</link>
      <pubDate>Tue, 08 Jan 2013 09:41:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/clue/</guid>
      <description>&lt;p&gt;Note to self: as an OSS project becomes more successful, the level of clue on the mailing lists drops.&lt;/p&gt;
&lt;p&gt;Corollary: a successful project will have a mailing list containing several knuckle-bitingly painful posts.&lt;/p&gt;
&lt;p&gt;Why is this? I believe that it&amp;rsquo;s all to do with the motivation of the person emailing the list. To begin with, an OSS project only has people who are very interested in using it posting to the lists. These people tend to be technically savvy and can identify what might be causing problems. The signal to noise ratio is therefore very favourable to a sensible discussion.&lt;/p&gt;
&lt;p&gt;As a project gets used more widely used, the people using it move from those who are actively interested to the people who are sitting around them. They may not be quite so engaged, but they&amp;rsquo;re probably still relatively tech savvy. They have far less incentive or desire to dig into the code and understand why things are failing. The level of clue on the mailing lists appears to drop.&lt;/p&gt;
&lt;p&gt;This process continues until the project is so successful that it&amp;rsquo;s a mandated part of people&amp;rsquo;s jobs. They have no choice but to use it. At this point, there is absolutely no incentive to understand &lt;em&gt;why&lt;/em&gt; things are not working as intended, and every incentive to try and see whether someone else has already solved the problem. The level of clue in the mailing lists appears to drop through the floor.&lt;/p&gt;
&lt;p&gt;I remind myself of this on a regular basis, and you know what? I&amp;rsquo;m okay with it too. In fact, sometimes I even smile when I see my project&amp;rsquo;s lists filled with poorly researched, ill considered emails. It&amp;rsquo;s a sign of success.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Where Computing Lives</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/where-computing-lives/</link>
      <pubDate>Tue, 08 Jan 2013 09:26:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/where-computing-lives/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s hardly an original thought, but it&amp;rsquo;s interesting to me that the location of a majority of someone&amp;rsquo;s computing power has moved from their personally controlled space to datacentres. It&amp;rsquo;s allowing companies to do some extraordinary data processing, but just surface the results to the user.&lt;/p&gt;
&lt;p&gt;To prove my case, I cite Google Now. Which is awesome, in the traditional meaning of the word. Now, if I could just make it stop accurately telling me that it&amp;rsquo;s time to &amp;ldquo;head home&amp;rdquo; and then highlight a nearby pub.&lt;/p&gt;
&lt;p&gt;The other thought that occurs to me is that the constraining factor on someone&amp;rsquo;s ability to utilise computational resources is going to become wireless bandwidth. I wonder whether there&amp;rsquo;s a Moore&amp;rsquo;s Law for that&amp;hellip;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Removing Java 7 From OS X</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/removing-java-7-from-os-x/</link>
      <pubDate>Mon, 07 Jan 2013 18:28:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/removing-java-7-from-os-x/</guid>
      <description>&lt;p&gt;As a follow-up from &amp;ldquo;&lt;a href=&#34;https://www.rocketpoweredjetpants.com/2013/01/switching-between-java-6-and-7-on-os-x.html&#34;&gt;Switching between Java 6 and 7 on OS X&lt;/a&gt;&amp;rdquo;, here&amp;rsquo;s how to remove Java 7 entirely:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# First the JDK  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /Library/Java/JavaVirtualMachines/  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo rm -rf jdk1.7&lt;span style=&#34;color:#ae81ff&#34;&gt;\*&lt;/span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# And now the JRE  &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd /Library/Internet&lt;span style=&#34;color:#ae81ff&#34;&gt;\\&lt;/span&gt; Plug-Ins/  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo rm -rf JavaAppletPlugin.plugin  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd ../PreferencePanes/  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo rm JavaControlPanel.prefpane 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that should be that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Help Me Help You</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/help-me-help-you/</link>
      <pubDate>Mon, 07 Jan 2013 13:18:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/help-me-help-you/</guid>
      <description>&lt;p&gt;You&amp;rsquo;ve finally got fed up enough with whatever your current problem is to ask for help from someone. That help could come from a number of sources, but for the sake of argument, let&amp;rsquo;s imagine that it&amp;rsquo;s a mailing list. Further, let&amp;rsquo;s imagine that it&amp;rsquo;s a mailing list that I&amp;rsquo;m on, and that I genuinely want to help you. Here&amp;rsquo;s how you can help me help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tell me what it is you&amp;rsquo;re actually trying to do, at a high level. Why? Because there might just be another way of approaching the problem that&amp;rsquo;s just as valid. An example? Sure: &amp;ldquo;I want to log into gmail via the web UI&amp;rdquo; That&amp;rsquo;s an inadvisable thing to do, especially when what you&amp;rsquo;re really wanting to do is verify that some email was sent by your system. &lt;/li&gt;
&lt;li&gt;Remember, you&amp;rsquo;ve been pushing hard to fix this problem for a while now. You&amp;rsquo;re probably reaching out to the group because you&amp;rsquo;ve run out of ideas; either that, or you&amp;rsquo;ve no idea how to begin solving the problem and really need some pointers. In both cases, you&amp;rsquo;re loaded with context that I&amp;rsquo;m lacking. Help provide that context. Tell me why other ideas haven&amp;rsquo;t worked. Perhaps it&amp;rsquo;d help to &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2013/01/talk-to-teddy.html&#34;&gt;talk to the teddy&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Information is vital. Without it, I can&amp;rsquo;t help you. A reproducible test case is one of the best ways to let me help you. Yes, yes, yes, I know that there&amp;rsquo;s absolutely no way that &lt;em&gt;anyone, anywhere&lt;/em&gt; could possibly see your Super Sekret company intranet. You&amp;rsquo;re a smart chap. Creating a reproducible test case is not normally that hard.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are the key things, and they can be encapsulated as &amp;ldquo;I&amp;rsquo;m not you.&amp;rdquo; There are other things that are more likely to get me interested in helping you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run a spell checker across your email. Just quickly.&lt;/li&gt;
&lt;li&gt;Read your email to yourself before sending it. You were most likely frustrated when you wrote it, and missed out something that might help provide more context to me.&lt;/li&gt;
&lt;li&gt;Read your email aloud before sending it. You were most likely very frustrated when you wrote it, if you&amp;rsquo;ve stumbled over things and not clearly explained them, I&amp;rsquo;ll just move on.&lt;/li&gt;
&lt;li&gt;I know you&amp;rsquo;re frustrated and annoyed. Calling the tool your working with &amp;ldquo;shit&amp;rdquo; or &amp;ldquo;appalling&amp;rdquo; or relying on gross over-generalisations (&amp;ldquo;clicking &lt;strong&gt;never&lt;/strong&gt; works&amp;rdquo;), insults, or, better yet, combining any random two (&amp;ldquo;only a flea bitten moron would think of using this pile of crap&amp;rdquo;) isn&amp;rsquo;t a way to make friends and influence people.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And, that, my friends, is all I have to say on that for now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Talk to the Teddy</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/talk-to-the-teddy/</link>
      <pubDate>Mon, 07 Jan 2013 13:09:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/talk-to-the-teddy/</guid>
      <description>&lt;p&gt;Ever hear of &amp;ldquo;talking to the teddy&amp;rdquo;? That&amp;rsquo;s the time honoured technique of explaining a particularly troubling and hasslesome technical issue to a colleague only to realize half-way through the explanation that the answer is obvious and the problem far tamer than you thought.&lt;/p&gt;
&lt;p&gt;You can save face and others&amp;rsquo; time by replacing a colleague with a small teddy bear. Explain your woes to that first. Otherwise, there&amp;rsquo;s a greater than even chance I might make strange noises and sit there, staring blankly at you the first time we talk about a problem.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Switching Between Java 6 and 7 on OS X Lion</title>
      <link>https://www.rocketpoweredjetpants.com/2013/01/switching-between-java-6-and-7-on-os-x-lion/</link>
      <pubDate>Wed, 02 Jan 2013 15:21:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2013/01/switching-between-java-6-and-7-on-os-x-lion/</guid>
      <description>&lt;p&gt;This took me too long to figure out, so I&amp;rsquo;m writing this up now.&lt;/p&gt;
&lt;p&gt;To switch from Java 7 to Java 6 on Lion:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo rm /System/Library/Frameworks/JavaVM.framework/Versions/Current  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ln -sf /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents  /System/Library/Frameworks/JavaVM.framework/Versions/Current  
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To switch from Java 6 to Java 7:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo rm /System/Library/Frameworks/JavaVM.framework/Versions/Current  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo ln -sf /System/Library/Frameworks/JavaVM.framework/Versions/A  /System/Library/Frameworks/JavaVM.framework/Versions/Current
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s something deeply suspicious about this, but at least it works.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ruminations on Code Bases I Have Known</title>
      <link>https://www.rocketpoweredjetpants.com/2012/11/ruminations-on-code-bases-i-have-known/</link>
      <pubDate>Sun, 04 Nov 2012 19:24:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/11/ruminations-on-code-bases-i-have-known/</guid>
      <description>&lt;p&gt;Code bases, eh? Can&amp;rsquo;t live with &amp;rsquo;em. Can&amp;rsquo;t live without &amp;rsquo;em. Though that&amp;rsquo;s not strictly true, as many generations have seen. Wait. Hang on. This isn&amp;rsquo;t quite the articulate start I pictured in my head. Let&amp;rsquo;s have another go at this.&lt;/p&gt;
&lt;p&gt;*ahem*&lt;/p&gt;
&lt;p&gt;The style of architecture used for a large code base largely depends on how the third party dependencies of that code base are managed. Specifically, whether those dependencies are handled at the global level or at the team/project level.&lt;/p&gt;
&lt;p&gt;Contentious statement out of the way. Let&amp;rsquo;s see if I can explain what the heck I just meant.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take the case of a large code base where dependencies are handled &amp;ldquo;globally&amp;rdquo;. For the sake of this discussion, let&amp;rsquo;s imagine that this means that there&amp;rsquo;s only ever one version of a particular dependency in use at any particular revision of that code base. When the code base is tiny, this approach is what teams tend towards (IME) and updating a dependency is relatively straightforward. However, as the code base grows, or more projects are rolled into it, updating a third party dependency becomes increasingly difficult and time consuming.&lt;/p&gt;
&lt;p&gt;When a code base reaches a certain size, a choice has to be made. Should third party dependencies be handled at the project level or for the code base as a whole? The downside of handling them for the code base as a whole is that updates appear to become increasingly costly. It therefore seems reasonable to split the code base up in some way and then let the chunks handle themselves.&lt;/p&gt;
&lt;p&gt;And this is where the problems start&amp;hellip;.&lt;/p&gt;
&lt;p&gt;If your code base is anything like many I&amp;rsquo;ve seen, there&amp;rsquo;s a suite of utility functions and library code that&amp;rsquo;s shared between projects. These are often referred to by their code names, but &lt;em&gt;everyone&lt;/em&gt; knows that they&amp;rsquo;re reusable components that should be (ummm&amp;hellip;) reused by other projects in the code base (let&amp;rsquo;s call them &amp;ldquo;client projects&amp;rdquo;). The problem is that each client project is now handling its own set of dependencies, so this suite of library functions must, by necessity, only have the bare minimum number of third party dependencies (to minimise the chances of accidentally requiring multiple versions of the same library in a client project) or be more permissive about dependencies at the cost of more painful integration with client projects later.&lt;/p&gt;
&lt;p&gt;Given the evolution of a code base, the former is preferred and the latter is what gets done, at least to start with. Put another way: by the time you realise that there&amp;rsquo;s a need for a shared set of utility functions, the helper dependencies have their tentacles well and truly wrapped round the code to share.&lt;/p&gt;
&lt;p&gt;But we know that it&amp;rsquo;s possible to reduce the third party dependencies on core library code to a minimum. &lt;a href=&#34;http://guava-libraries.googlecode.com/&#34;&gt;Guava libraries&lt;/a&gt;, which Google uses, demonstrates this. (Pro tip: to get there, never use XML)(only kidding)(not really: hi xalan and xerces and xml-api.jar) So it&amp;rsquo;s demonstrably possible to have a tiny subset of your code base be sharable without causing a ton of grief when integrating new versions. Hadanza!&lt;/p&gt;
&lt;p&gt;That example with the common library of common code (let&amp;rsquo;s call it Odin or something suitably grandiose) is a microcosm of the horror that awaits when reintegrating different projects that have been managing their dependencies separately for some time. Each time there&amp;rsquo;s a third party dependency shared by each project being integrated but at a different version there&amp;rsquo;s more than just code integration to do. Some parts of one or both projects need to be reworked or rewritten, which increases the amount of time and testing required to do the reintegration.&lt;/p&gt;
&lt;p&gt;Given the pain of updating even multiple lagging dependency can cause, it&amp;rsquo;s far more likely that the client projects, once split out of trunk, will remain split out indefinitely. Which is fine, until they need to communicate.&lt;/p&gt;
&lt;p&gt;One approach to resolve the problem of communicating between different projects at runtime is to use XML. This approach is great, unless schema validation is turned on. Then at least one side or the other is going to claim that a perfectly valid message is complete garbage. Well, nuts! So, let&amp;rsquo;s not validate our XML, but use something like XPath to pull out the bits that we find interesting (using something like &lt;a href=&#34;http://www.eccnet.com/schematron/index.php/Main_Page&#34;&gt;Shcematron&lt;/a&gt; perhaps, though it&amp;rsquo;s been a looong time since I looked at it) The alternative is to use something like &lt;a href=&#34;http://protobuf.googlecode.com/&#34;&gt;Protocol Buffers&lt;/a&gt; or &lt;a href=&#34;http://thrift.apache.org/&#34;&gt;Thrift&lt;/a&gt;, which are used by Google and, given the latter&amp;rsquo;s origin, Facebook.&lt;/p&gt;
&lt;p&gt;OK. So the various client projects now have a robust mechanism for communicating: by passing some sort of message between instances. That message may take the form of a document (recommended, as this minimises the surface area of API that needs to be agreed between client and server) or an RPC call (less recommended, as it&amp;rsquo;s all too easy to accidentally tightly bind two communicating projects together). As with all things IT-related, it&amp;rsquo;s typically easier to do the less recommended thing.&lt;/p&gt;
&lt;p&gt;The astute reader will already be seeing that this is describing an SOA-style architecture. The astute reader would be correct. The astute reader may now have a cookie. Unless they&amp;rsquo;re in the UK, in which case they&amp;rsquo;ll have to give permission for cookies to be used. (joke)(not really). And yes, I know that expanding &amp;ldquo;SOA-style architecture&amp;rdquo; leads to a nonsensical phrase. I can live with that.&lt;/p&gt;
&lt;p&gt;So: a large code base consisting of lots of independent projects each managing their own third party dependencies likely leads to an SOA-style architecture. Or integration through the database. I know which one I&amp;rsquo;d prefer.&lt;/p&gt;
&lt;p&gt;But what if we stick with the original plan of managing the third party dependencies globally? This leaves us with more options (side note: I like having more options) We&amp;rsquo;ll still need some mechanism for old versions of a project within this one tree to communicate with newer versions of itself (if only across updates to the software, but also if you&amp;rsquo;re scaling horizontally), and, again, XML, protobufs, thrift or some other data interchange format will help a lot here. So, we &lt;em&gt;could&lt;/em&gt; also make use of SOA-style architecture, and that may be advisable.&lt;/p&gt;
&lt;p&gt;Alternatively, given that everything is using the same third party dependencies it&amp;rsquo;s likely that it&amp;rsquo;s a lot easier to share code between projects within the same tree. Depending on the rigour of code reviews or tools put in place to prevent teams delving into &amp;ldquo;not public really&amp;rdquo; APIs another style would be to just use other projects within the same tree in exactly the same way as third party dependencies.&lt;/p&gt;
&lt;p&gt;Groovy. So we could use SOA or have a mass of tangled projects? Well, neither sounds that appealing to be honest. So which approach should be chosen?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take our thought experiment through a required update of a third party dependency (let&amp;rsquo;s imagine that not updating the library will accidentally cause a portal to Help Desk to be opened through which The Angry Users may reach you directly)&lt;/p&gt;
&lt;p&gt;In the case of everyone being in the same tree it&amp;rsquo;s likely you&amp;rsquo;re already close to the latest release, as there&amp;rsquo;s always &lt;em&gt;someone&lt;/em&gt; who wants that latest whizz-bang feature. If you&amp;rsquo;ve lots of dependencies &amp;mdash; and you probably do &amp;mdash; then an update to a third party dependency may be a well practised thing. Glibly speaking, the chances are that the update, whilst not necessarily smooth, can be managed with only minimal wailing and gnashing of teeth.&lt;/p&gt;
&lt;p&gt;In the other world, of lots of separate projects each managing their own dependencies, things might well be very different. If each project has assiduously been updating their third party dependencies to the latest and greatest release, then things might be easier than in the case of a single, unified tree as you&amp;rsquo;re attempting to update a smaller code base. Hurrah!&lt;/p&gt;
&lt;p&gt;However, it was the pain of these library updates that caused the code base to fracture into a world where SOA made sense. Some poor bastard is going to have to do a massive update, and that&amp;rsquo;s going to hurt. IME, the pain of an update grows non-linearly and at a multiple greater than 1 in relation to the number of intervening revisions skipped. That is, skipping one revision hurts, skipping (say) five really hurts and skipping a major version may well be justifiable cause for murder when it comes to integrate the latest and greatest into your tree (aside: that&amp;rsquo;s why people who are gung-ho about branching recommend integrating branches as frequently as makes sense) To make matters more painful, this integration is not being done at a time of the project&amp;rsquo;s choosing: it&amp;rsquo;s being crammed into an already too full release schedule. Yay! A recipe for success if there ever was one.&lt;/p&gt;
&lt;p&gt;Taking this back to the start of this post, I hope you can now see that. in my view, how third party dependencies are handled really do have an impact on the architecture of your system.&lt;/p&gt;
&lt;p&gt;Now, I&amp;rsquo;ve written this &amp;ldquo;in the Yegge style&amp;rdquo; (lots of words, aided by a glass or two of wine), so I won&amp;rsquo;t be offended if you all pull this apart in the comments :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Time Off</title>
      <link>https://www.rocketpoweredjetpants.com/2012/10/time-off/</link>
      <pubDate>Wed, 03 Oct 2012 20:10:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/10/time-off/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been quiet here, and its going to get quieter here for a bit longer: I&amp;rsquo;m taking some time off to spend with The Boy and The Wife. I&amp;rsquo;ll be back in time for the W3C TPAC. Have fun, everyone. Try not to break anything while I&amp;rsquo;m not paying attention :)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Future of Mobile Testing</title>
      <link>https://www.rocketpoweredjetpants.com/2012/09/the-future-of-mobile-testing/</link>
      <pubDate>Tue, 18 Sep 2012 17:33:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/09/the-future-of-mobile-testing/</guid>
      <description>&lt;p&gt;Six or so years ago, Jason Huggins and I were talking about the next generation of web testing tools. This wasn&amp;rsquo;t a conversation about the as-then unreleased &lt;a href=&#34;http://seleniumhq.wordpress.com/2010/02/22/selenium-1-0-2-release-firefox-3-6-and-snow-leopard-support/&#34;&gt;Selenium 1.0&lt;/a&gt; or even of &lt;a href=&#34;http://google-opensource.blogspot.co.uk/2009/05/introducing-webdriver.html&#34;&gt;WebDriver&lt;/a&gt;, which was a new and shiny thing I was working on at &lt;a href=&#34;http://www.thoughtworks.com/&#34;&gt;ThoughtWorks&lt;/a&gt;. This was about the &lt;em&gt;next&lt;/em&gt; generation of testing tools.&lt;/p&gt;
&lt;p&gt;The fact that we can do automated testing on the Web is a happy accident. When MS and Netscape put Javascript into browsers and standardised the DOM they didn&amp;rsquo;t do so with an eye to making it easy to write tests. They wanted new and whizzy features added that only worked in their browser in a fight to win the browser wars. Each browser implemented the features of the other and then added more in a bid to gain the edge. The fact that it was possible to build something like Selenium on top of this work was never meant to happen.&lt;/p&gt;
&lt;p&gt;That wasn&amp;rsquo;t the conversation that Jason and I were having. We were talking about what the next generation of testing tools would use; the ones that would make selenium and webdriver totally redundant. It was obvious to both of us that accessibility APIs would be The Way Forward. After all, users with one form of disability or another make up a small, but important, percentage of web users. Their equal access to information and applications are enshrined in laws. Not only is making an app accessible a groovy and lovely thing to do, it&amp;rsquo;s also often a legal requirement.&lt;/p&gt;
&lt;p&gt;The next generation of tools, we reasoned, would build upon this accidental automation infrastructure in the same way that we used the DOM and JS: to provide an API that can be used to drive and query an application from outside that application.&lt;/p&gt;
&lt;p&gt;Microsoft lead the way, when .Net 3.0 contained an API called &lt;a href=&#34;http://msdn.microsoft.com/en-us/library/ms747327.aspx&#34;&gt;UI Automation&lt;/a&gt;. I was working on a project with Mike Two, who hacked together a proof of concept against the desktop app we were working on before flying to India to be closer to the dev team there. Some time later, &lt;a href=&#34;https://github.com/TestStack/White&#34;&gt;White&lt;/a&gt; appeared, which took the concepts and followed through. Brilliant stuff. &lt;/p&gt;
&lt;p&gt;Then it went quiet.&lt;/p&gt;
&lt;p&gt;Until, that is, the mobile revolution started. For an amazing number of users, their primary contact with the Web will be a mobile device, probably either Android or iOS. The problem is that neither of these platforms have &amp;ldquo;making it easy to write an automated end to end test&amp;rdquo; baked in as a concept. Increasingly, however, they do have the keys to accidental testability provided: their accessibility frameworks, and these are often called something useful like &amp;ldquo;&lt;a href=&#34;http://developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/UIAutomationRef/_index.html&#34;&gt;UI Automation&lt;/a&gt;&amp;rdquo; &lt;/p&gt;
&lt;p&gt;All this means that the next generation of tools are coming.&lt;/p&gt;
&lt;p&gt;There is, however, a missing piece. We have the &lt;a href=&#34;https://www.selenium.dev/selenium/docs/api/java/&#34;&gt;WebDriver APIs&lt;/a&gt; for testing web-based content, and for testing native content we have the accessibility APIs (which can be wrapped to &lt;a href=&#34;http://nativedriver.googlecode.com/&#34;&gt;look webdriver-ish&lt;/a&gt; if desired)(and I think it is desired)(but I&amp;rsquo;m biased) But how do these two gel? How do we test a &amp;ldquo;hybrid&amp;rdquo; app, composed of both native and web-based content? In my view, this gap can best be bridged by augmenting the accessibility API to allow a webdriver instance to be returned from any WebViews that are found via the accessibility APIs, and by allowing returned &lt;a href=&#34;https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/WebElement.html&#34;&gt;WebElement&lt;/a&gt; instances to also implement the equivalent of &lt;a href=&#34;http://developer.apple.com/library/ios/#documentation/ToolsLanguages/Reference/UIAElementClassReference/UIAElement/UIAElement.html#//apple_ref/javascript/cl/UIAElement&#34;&gt;UIAElement&lt;/a&gt;, so that it can be the target of OS-level simulated user inputs.&lt;/p&gt;
&lt;p&gt;There. Problem solved.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>What My Leaving Google Means for Selenium</title>
      <link>https://www.rocketpoweredjetpants.com/2012/09/what-my-leaving-google-means-for-selenium/</link>
      <pubDate>Sun, 16 Sep 2012 15:54:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/09/what-my-leaving-google-means-for-selenium/</guid>
      <description>&lt;p&gt;TL;DR: nothing.&lt;/p&gt;
&lt;p&gt;Longer version:&lt;br&gt;
One of the questions I&amp;rsquo;ve received most since announcing that I was leaving Google was &amp;ldquo;what does this mean for the &lt;a href=&#34;http://selenium.dev/&#34;&gt;Selenium project&lt;/a&gt;?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My answer is &amp;ldquo;not much&amp;rdquo; The project itself is Open Source, and more than half the commits are coming from developers outside of Google. These developers range from people working at other browser vendors (notably Mozilla and Opera) to people who are just interested in the project and write amazing code. Those people aren&amp;rsquo;t going away. The OSS project has already demonstrated that if I&amp;rsquo;m not around things still get done (though the releases slow waaaay down :), but I&amp;rsquo;ll still be reading every commit and still contributing where I can. Better still, I&amp;rsquo;m just one person, and the project is vibrant and humming with activity.&lt;/p&gt;
&lt;p&gt;What about the browser vendors? One obvious impact might be that the &lt;a href=&#34;https://chromedriver.chromium.org/&#34;&gt;ChromeDriver&lt;/a&gt; stops moving forward. That&amp;rsquo;s deeply unlikely to happen: the chrome driver is maintained as part of the &lt;a href=&#34;http://www.chromium.org/&#34;&gt;chromium&lt;/a&gt; OSS project by members of the Chrome team itself, rather than the team I was TL of (Browser Infrastructure at the last name change) This is also true of the &lt;a href=&#34;https://github.com/operasoftware/operachromiumdriver&#34;&gt;OperaDriver&lt;/a&gt;, which is maintained by Opera Software, and will be true of the FirefoxDriver once the &lt;a href=&#34;https://wiki.mozilla.org/Auto-tools/Projects/Marionette&#34;&gt;Marionette&lt;/a&gt; project is available on release builds. The trend we&amp;rsquo;ve been encouraging is that browser vendors should be responsible for their drivers &amp;mdash; given the complexity of the task, this is the best way to ensure that what our users want is what our users can actually do &amp;mdash; and the browser vendors are rising to the challenge.&lt;/p&gt;
&lt;p&gt;Of course, this does beg the question of what my team at Google does. The answer is &amp;ldquo;a heck of a lot&amp;rdquo;. Google has made a massive investment in browser automation. A lot of that investment is visible in the contributions to the Selenium project (where most of the team have earned the commit bit), but it can also be seen in other projects such as &lt;a href=&#34;https://github.com/google/wicked-good-xpath&#34;&gt;Wicked Good XPath&lt;/a&gt;, &lt;a href=&#34;http://code.google.com/p/puppeteer/&#34;&gt;Web Puppeteer&lt;/a&gt; and the &lt;a href=&#34;http://code.google.com/p/selenium/wiki/AutomationAtoms&#34;&gt;Browser Automation Atoms&lt;/a&gt;. The team also works on integrating these APIs with Google&amp;rsquo;s infrastructure, and providing support and guidance to teams, and they&amp;rsquo;re constantly striving to make writing web tests so stable and easy that even a software engineer can write them :) I may have left that team, but that investment continues unabated.&lt;/p&gt;
&lt;p&gt;Sotto voce: if you&amp;rsquo;d like to join that team, I can pass on your CV&amp;hellip;.&lt;/p&gt;
&lt;p&gt;Which leaves my involvement with the &lt;a href=&#34;http://w3c.org/TR/webdriver&#34;&gt;W3C spec&lt;/a&gt;. It&amp;rsquo;s true, over the next couple of months I intend to spend a lot of time with my family, but I&amp;rsquo;m also planning on spending time working on the spec. Once I join Facebook, I fully intend to continue co-editing it. That work is going to continue.&lt;/p&gt;
&lt;p&gt;So overall, my take is that my leaving Google isn&amp;rsquo;t going to have an appreciable effect on the OSS project or the spec.&lt;/p&gt;
&lt;p&gt;Which is nice.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hidden Options in javac</title>
      <link>https://www.rocketpoweredjetpants.com/2012/08/hidden-options-in-javac/</link>
      <pubDate>Sat, 25 Aug 2012 17:07:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/08/hidden-options-in-javac/</guid>
      <description>&lt;p&gt;You probably already know that the default Java compiler (javac) can output a bunch of options if you run it as &lt;code&gt;javac -h&lt;/code&gt;. You can also get the extended list of Oracle specific command switches using &amp;ldquo;javac -X&amp;rdquo;. Until today, I&amp;rsquo;d not realised that javac has even more command line switches, called &amp;ldquo;&lt;a href=&#34;http://hg.openjdk.java.net/jdk7/2d/langtools/file/eaf608c64fec/src/share/classes/com/sun/tools/javac/main/RecognizedOptions.java&#34;&gt;hidden options&lt;/a&gt;&amp;rdquo;. It&amp;rsquo;s possible to call them using a syntax like so &lt;code&gt;-XD-hidden-option-name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, I wonder if ecj has the same sort of thing. It&amp;rsquo;d be handy&amp;hellip;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; it doesn&amp;rsquo;t look like it. *unhappy face*&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ant&#39;s JUnit is a Horse&#39;s Arse</title>
      <link>https://www.rocketpoweredjetpants.com/2012/08/ants-junit-is-a-horses-arse/</link>
      <pubDate>Sun, 12 Aug 2012 04:37:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/08/ants-junit-is-a-horses-arse/</guid>
      <description>&lt;p&gt;There are insightful posts, and then there&amp;rsquo;s this&amp;hellip;.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a story &amp;ldquo;&lt;a href=&#34;http://www.astrodigital.org/space/stshorse.html&#34;&gt;somewhere on the Web&lt;/a&gt;&amp;rdquo; that builds a case for the size of the boosters for NASA&amp;rsquo;s shuttle being limited in size by the smallest train tunnel that they needed to pass through. That tunnel was slightly wider than a train track, and, ultimately, the width of that train track was determined by the width of a horse&amp;rsquo;s arse.&lt;/p&gt;
&lt;p&gt;I was reminded of this in a meeting recently when someone asked about how continuous build systems provide results of tests to users. Almost all of them do so by looking for XML files generated by JUnit. Specifically, they look for those files in the format generated by &lt;a href=&#34;http://ant.apache.org/&#34;&gt;Ant&amp;rsquo;s&lt;/a&gt; JUnit runner. It doesn&amp;rsquo;t matter how sophisticated that continuous build server is, it still needs to read files in that format. In a way, the JUnit runner in Ant is a horse&amp;rsquo;s arse too.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Routes to Faster Java Compilation</title>
      <link>https://www.rocketpoweredjetpants.com/2012/07/routes-to-faster-java-compilation/</link>
      <pubDate>Sat, 28 Jul 2012 18:39:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/07/routes-to-faster-java-compilation/</guid>
      <description>&lt;p&gt;My recent post comparing the compilation speed of the &lt;a href=&#34;https://www.rocketpoweredjetpants.com/2012/07/compilation-speed-of-ecj-vs-javac.html&#34;&gt;ecj batch compiler and javac&lt;/a&gt; hints at the topic that I&amp;rsquo;ve been starting to explore in my copious free time: how to get a faster build out of a java project. This matters to me a lot, since I spend the majority of my day not answering emails coding in Java for an &lt;a href=&#34;http://selenium.dev/&#34;&gt;OSS project&lt;/a&gt;. Here&amp;rsquo;s where I&amp;rsquo;m at now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Develop as much as possible in the IDE, including running tests. If there&amp;rsquo;s some way to avoid doing a command line build, then avoiding it allows faster progress. On the Selenium project there&amp;rsquo;s a reasonable amount of effort invested to make this possible. It&amp;rsquo;s mostly successful, but not as fast as I&amp;rsquo;d like because&amp;hellip;&lt;/li&gt;
&lt;li&gt;Minimize the dependencies in your code base. The IDE based test runs I do often need to shell out to build things like firefox extensions. That kills the performance of the build and makes doing end-to-end testing far less pleasant.&lt;/li&gt;
&lt;li&gt;Delete dead code. The less there is to compile, the better.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;http://dan.bodar.com/2012/02/28/crazy-fast-build-times-or-when-10-seconds-starts-to-make-you-nervous/&#34;&gt;Evaluate the tool chain&lt;/a&gt;. Past assumptions don&amp;rsquo;t always hold true, so revisit them from time to time.&lt;/li&gt;
&lt;li&gt;Going from Java to a JAR is almost always the right approach, particularly if you&amp;rsquo;re dealing with a large number of files. Running &amp;ldquo;stat&amp;rdquo; on each java file and class file and output JAR file is often less efficient than running &amp;ldquo;stat&amp;rdquo; on just the java files and the output JAR. However&amp;hellip;&lt;/li&gt;
&lt;li&gt;Use an SSD. Compiling code is an exercise in small and random reads and writes. A spinning platter disk isn&amp;rsquo;t the best choice for this. I need to rerun my checks to see if the statting of class files is now worth the extra effort.&lt;/li&gt;
&lt;li&gt;Get more memory. Your OS will cache things pretty aggressively, but hitting swap will murder your build times.&lt;/li&gt;
&lt;li&gt;More memory and an SSD offset many of the disadvantages of a slower CPU, particularly in a single-threaded build.&lt;/li&gt;
&lt;li&gt;Build in parallel. If you don&amp;rsquo;t, then make sure the clock speed of your CPU is as high as possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, I&amp;rsquo;d love to say that I do all of these things, but I can&amp;rsquo;t. In particular, I don&amp;rsquo;t build in parallel, which is a real waste of 7 of the cores in my personal machine (or even more for my machine at work) The main reason for that is that the build tool that I use (rake) has fairly blunt support for running in parallel and the &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/wiki/Crazy-Fun-Build&#34;&gt;layers of abstraction wrapped around it&lt;/a&gt; make figuring out when to use &lt;a href=&#34;http://rake.rubyforge.org/files/lib/rake_rb.html#M000005&#34;&gt;multitasks&lt;/a&gt; harder.&lt;/p&gt;
&lt;p&gt;The thing that sticks with me the most, however, is that compilation speeds are fastest when the compiler only has a few files to deal with. With Java, that leads to having lots of small targets rather than one massive glob of the entire file system. Or it would if the way that determining whether a JAR had changed was determined by something other than the last modified time of the file. There&amp;rsquo;s an additional wrinkle: how do you avoid a small change in a method (say adding a logging statement) from causing a complete recompilation of everything that depends on the JAR that contains that method? Hmmm&amp;hellip; &lt;a href=&#34;http://google-engtools.blogspot.com/2011/08/build-in-cloud-how-build-system-works.html&#34;&gt;I wonder&lt;/a&gt;&amp;hellip;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Compilation Speed of ecj vs javac</title>
      <link>https://www.rocketpoweredjetpants.com/2012/07/compilation-speed-of-ecj-vs-javac/</link>
      <pubDate>Tue, 24 Jul 2012 13:44:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/07/compilation-speed-of-ecj-vs-javac/</guid>
      <description>&lt;p&gt;For the longest time, I&amp;rsquo;ve taken it as an article of faith that the eclipse compiler (ecj) was significantly faster than the default Sun java compiler (javac). The last time I measured this properly was when I compared the Java5 compiler against the equivalent ecj release. After a recent conversation, I realised that I was still making the same assertion, despite over two years having elapsed.&lt;/p&gt;
&lt;p&gt;So, time to measure again. And this time, the results surprised me. What used to be a whitewash for ecj has now changed to a slight edge being given for javac.&lt;/p&gt;
&lt;p&gt;The test was done by repeating a build of the java components of the selenium codebase 20 times on an OS X machine running Lion and the latest released version of Java 7 from Oracle. The machine itself has 12GB of RAM and the development wasn&amp;rsquo;t being done on an SSD. The results were:&lt;/p&gt;
&lt;p&gt;javac: avg. 29.9s with a standard deviation of 0.80s&lt;br&gt;
ecj: avg. 31.1s with a standard deviation of 0.82s&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hadanza! First Public Working Draft</title>
      <link>https://www.rocketpoweredjetpants.com/2012/07/hadanza-first-public-working-draft/</link>
      <pubDate>Sun, 22 Jul 2012 21:16:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/07/hadanza-first-public-working-draft/</guid>
      <description>&lt;p&gt;Hadanza! The webdriver spec has reached &lt;a href=&#34;http://www.w3.org/TR/webdriver/&#34;&gt;First Public Working Draft&lt;/a&gt; on the 10th July, 2012. Now all we need to do is finish it :)&lt;/p&gt;
&lt;p&gt;/me does happy dance.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Explaining the Speed of Selenium Updates</title>
      <link>https://www.rocketpoweredjetpants.com/2012/07/explaining-the-speed-of-selenium-updates/</link>
      <pubDate>Sun, 22 Jul 2012 20:49:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/07/explaining-the-speed-of-selenium-updates/</guid>
      <description>&lt;p&gt;With the &lt;a href=&#34;http://seleniumhq.wordpress.com/2011/07/08/selenium-2-0/&#34;&gt;release of Selenium 2&lt;/a&gt;, we moved from incredibly slow updates and releases to far more regular ones. We were aiming for weekly releases, but have settled into something that averages between two and three weeks. That&amp;rsquo;s a pretty fast update schedule. Why do we do it?&lt;/p&gt;
&lt;p&gt;The first reason is that there are a lot of checkins every week on the project. Last week, which admittedly was pretty busy, saw 94 separate revisions in the space of the 7 days between the 14th and the 21st of July 2012. The previous week was quieter. There were 84 checkins. Each of these checkins represents either a refinement of existing functionality, a bug fix or (increasingly rarely) new features. We&amp;rsquo;d like to get those features and fixes in front of the users as quickly as possible.&lt;/p&gt;
&lt;p&gt;The second reason is that we need to continue working with the latest and greatest versions of browsers that are out there. Although Internet Explorer is on a comparatively leisurely yearly release cycle, Chrome and Firefox release a new major version once every six weeks. Six weeks! Each of these releases tends to call for a new selenium release too.&lt;/p&gt;
&lt;p&gt;One of the nice side-effects of the &lt;a href=&#34;https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol&#34;&gt;design of the webdriver APIs&lt;/a&gt; is that we can, in theory, decouple the release cycle of the individual browser drivers form the release cycle of the client libraries. Since the Chrome team maintain the &lt;a href=&#34;https://chromedriver.chromium.org/downloads&#34;&gt;chromedriver&lt;/a&gt;, this has been the case for a long time with chrome (hurrah!). Opera were the ones that blazed this particular trail, and the &lt;a href=&#34;https://github.com/operasoftware/operachromiumdriver&#34;&gt;opera driver releases&lt;/a&gt; are independent too. With the next release of Selenium, this will also be true for all client languages for IE as well.&lt;/p&gt;
&lt;p&gt;Which leaves Firefox.&lt;/p&gt;
&lt;p&gt;If we were happy with only using synthesized events, we could relax a little with the selenium releases. The problem is, we&amp;rsquo;re not happy with only using synthesized events, and neither are our users. Right now, the OS-level events are fired using a binary component built by the selenium project. The way that these components work means that we need to do a new release for every major number release of firefox. Back in the day, this meant &amp;ldquo;almost never&amp;rdquo;. Today, that means &amp;ldquo;once every six weeks&amp;rdquo;. It&amp;rsquo;s tiring for us. I&amp;rsquo;m sure it&amp;rsquo;s tiring for Mozilla. It&amp;rsquo;s definitely tiring for our users.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re working with Mozilla to try and get a mechanism for injecting events at the OS level built into Firefox itself. Once this mechanism is in place, we should be able to slow our release schedule a little (though we&amp;rsquo;d still like regular releases in order to get bug fixes in front of you)&lt;/p&gt;
&lt;p&gt;The other thing that Mozilla are working on is the &lt;a href=&#34;https://wiki.mozilla.org/Auto-tools/Projects/Marionette&#34;&gt;Marionette project&lt;/a&gt;. This is an implementation of the webdriver APIs embedded within Firefox (and mobile, and Firefox OS) and maintained by Mozilla themselves. Once this in place, the selenium project will be able to completely decouple releases of the client libraries from the browser drivers.&lt;/p&gt;
&lt;p&gt;This decoupling should be a Good Thing for users. For a start, it&amp;rsquo;ll be possible to do far more focused updates. For example, let&amp;rsquo;s imagine that we discover and fix a bug that only affects IE users. We could release a new IE driver server as soon as the fix is in and users can grab that, without also needing to grab a new version of the client libraries (or the Firefox driver&amp;hellip;.)&lt;/p&gt;
&lt;p&gt;Oh well. We&amp;rsquo;ll see.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Getting the show back on the road</title>
      <link>https://www.rocketpoweredjetpants.com/2012/07/getting-the-show-back-on-the-road/</link>
      <pubDate>Sun, 22 Jul 2012 20:27:00 +0100</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/07/getting-the-show-back-on-the-road/</guid>
      <description>&lt;p&gt;My old domain name &amp;mdash; pubbitch.org &amp;mdash; lapsed due to some pretty unfortunate circumstances. It&amp;rsquo;s taken me longer than I wanted to get a new domain name and set up a site, but I took a few hours out today to get something basic up and running.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s actually been remarkably painless. I&amp;rsquo;m using Google services for basically the entire thing, with the main site being hosted on App Engine (well, I say &amp;ldquo;main site&amp;rdquo;, but it&amp;rsquo;s actually just an index page right now) and this blog on Blogger. I&amp;rsquo;ll be ironing out the rough edges as I go along, but this should be my new home on the Net from now on.&lt;/p&gt;
&lt;p&gt;Yay!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Working on Specs</title>
      <link>https://www.rocketpoweredjetpants.com/2012/01/working-on-specs/</link>
      <pubDate>Tue, 03 Jan 2012 16:24:00 +0000</pubDate>
      <guid>https://www.rocketpoweredjetpants.com/2012/01/working-on-specs/</guid>
      <description>&lt;p&gt;Well, that makes it real:&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://dvcs.w3.org/hg/webdriver/raw-file/d2a77f663f2e/webdriver-spec.html&#34;&gt;http://dvcs.w3.org/hg/webdriver/raw-file/d2a77f663f2e/webdriver-spec.html&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss> 
