Running|Code About software and adventures on foot. https://www.runningcode.net Me and the Mac at 40 <p><a href="https://mac40th.com">The Mac is 40 years old</a>. Like myself, it’s an elder millennial in age and spirit.</p> <p>Time to reflect.</p> <p>My first experience with the Mac was in the middle school computer lab: rows of beige plastic in various flavors of Macintosh LC and Color Classic.</p> <aside><p> In hindsight it was a pretty great lab, well-stocked for a small-town school far from any city and always a decade behind the times. </p></aside> <p>The Macs at the lab were utterly unlike the 386 at home. My first experiences of the real Web—color graphics, Netscape—were on a Mac. <a href="https://groups.csail.mit.edu/mac/users/bob/clarisworks.php">ClarisWorks</a> my first encounter with a modern word processor. Lemmings and Carmen Sandiego! These machines were cheerful portals to a larger world, they invited creative experimentation and learning, they offered hopeful hints of the future.</p> <p>But most importantly, these Macs had <a href="https://hypercard.org">HyperCard</a>.</p> <p>The Spanish teacher delivered our weekly quizzes on HyperCard stacks. She wrote and distributed them herself! Soon I was building my own stories and games and little worlds in HyperCard. I knew I was playing with magic, but I didn’t know it was my first step to a life of building software and digital creation.</p> <aside><p> Other early Macintosh experiences: pouring hours into MacPaint while visiting a family friend with an <a href="https://lowendmac.com/2014/powerbook-history-before-the-g3/">old PowerBook</a>, a tiny black plastic miracle with a soft grayscale TFT display. A relative’s Bondi Blue. Staring at the lampshade <a href="https://www.imore.com/mac/imac/using-a-g4-imac-in-2023-to-mark-its-20th-anniversary">iMac G4</a> in the college library basement. </p></aside> <p>About a decade later, I bought my first Mac in preparation for working with grad school’s Unix systems, an aluminum PowerBook G4. Mac OS X Tiger: what a perfect entry point to the modern Apple ecosystem.</p> <aside><p> Since then: a 2009 Intel iMac, a first-gen retina MacBook Pro, M1 iMac, and various work-furnished MacBooks Pro. </p></aside> <p>A lot has changed, but much has not changed.</p> <p>Time to make something new again.</p> Mon, 29 Jan 2024 00:00:00 +0000 https://www.runningcode.net/2024/01/29/mac-40/ https://www.runningcode.net/2024/01/29/mac-40/ Accumulation, or: Time, Stopped <p>In which I find an excuse to use the word “unmoored”.</p> <style type="text/css"> a.big-shiny-button { color-scheme: light; display: inline-block; border-width: 16px; border-radius: 12px; border-style: outset; border-color: var(--text-color-main); } .big-shiny-button span { display: inline-block; border-color: var(--text-color-main); } .big-shiny-button > span { border-width: 10px; border-style: double; } .big-shiny-button > span > span { border-width: 6px; border-style: inset; background-color: var(--background-color-header); } .big-shiny-button > span > span > span { border-width: 2px; border-style: solid; background-color: hsla(0, 0%, 100%, 0.4); padding: 0.5em 1em; } .big-shiny-button > span > span > span::before { content: "☞ "; } .big-shiny-button > span > span > span::after { content: " ☜"; } </style> <p><a class="big-shiny-button" href="https://www.runningcode.net/accumulation/"><span><span><span>Continue</span></span></span></a></p> Thu, 23 Feb 2023 00:00:00 +0000 https://www.runningcode.net/2023/02/23/accumulation/ https://www.runningcode.net/2023/02/23/accumulation/ Building a Computer From Scratch: Level 1 <p>Follow along as we build a fake computer architecture and assembly language from scratch, with a virtual machine/interpreter written in JavaScript. Why? I saw a fun YouTube video that posed the question: <a href="https://youtu.be/ExwqNreocpg">“Can you fit a whole game into a QR code?”</a>. Long story short, this intersected with my hobby of <a href="https://github.com/mmertsock/eskergames">dabbling with JavaScript games and game engines</a> and some recent reading about 16-bit era games being written in assembly code, forming this Venn diagram in my mind:</p> <figure> <a href="https://www.runningcode.net/assets/posts/mountain-goat-venn-2x.png"><img src="https://www.runningcode.net/assets/posts/mountain-goat-venn-2x.png" srcset="https://www.runningcode.net/assets/posts/mountain-goat-venn.png 1x, https://www.runningcode.net/assets/posts/mountain-goat-venn-2x.png 2x" alt="Venn diagram, three circles labeled (1) 'Cool idea: embed a game in a QR code'. (2) 'Hobby: tinkering with with JS games and game engines'. (3) 'Reading about: 16-bit era games written in assembly'. Intersection labeled 'Build my own assembly language, computer architecture, virtual machine, in JS, from scratch'" /></a> </figure> <p>Basically, I’ve always been interested in game development, but I always stall working on actual games and become more invested in tinkering with the systems and frameworks that games are built with.</p> <p>Anyway, in the video, embedding was achieved by constructing a QR code that encoded the raw binary content of an x86 executable file. How would this work for a JavaScript/HTML game? A straightforward answer: just encode the text of an HTML document with inline JavaScript into the QR code. That’s a lot of code to embed. So another option is encode just the JavaScript in the QR code, and then have a web page run <code class="language-plaintext highlighter-rouge">eval</code> on that code. But that’s an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval">“enormous security risk”</a> 😰. Then I thought about those old game cartridges hand-coded in assembly and executed on a console in a dedicated runtime. And there’s the recent trend of deliberately constrained game environments like Pico-8. So I thought it would be fun to make a simulated computer where the “cartridge” is a QR code. Or at least, it’s an excuse to play around with designing a system at a <em>much</em> lower level than my everyday work.</p> <p>So here’s some blogging about my experiments with this idea.</p> <blockquote class="warn"> 🤷🏼 <em>First, some massive caveats:</em> Don't take any of this seriously. Nothing you see here is going to be rigorous or correct. This is all based on a few courses in assembly language and language theory long ago during college and grad school, some experience writing apps in C and Fortran, and just the general vibes I have from years of torturing sand. It will be built incrementally, only what's necessary to make something interesting work. But maybe we'll learn some things. </blockquote> <p>What do we need in order to build it? Maybe this:</p> <ul> <li>We need to design a <a href="https://en.wikipedia.org/wiki/Instruction_set_architecture">computer architecture</a>.</li> <li>Define machine instructions that it understands.</li> <li>And also design an assembly language that maps to that machine language, because we want our code to be human-readable.</li> <li>The machine needs some form of memory.</li> <li>And it needs a system for input and output.</li> <li>Then we will have an interpreter that runs inside an actual web browser, which constructs this virtual machine, feeds it assembly code, attaches inputs to browser input events, and somehow attaches output to the DOM.</li> <li>We’re not going to go too far down in the simulation. Not constructing logic gates or transistors or adder circuits. Might gloss over a lot of the details at the level of individual bits.</li> <li>Is that all?</li> </ul> <h2 id="level-1-setup-accumulator-addition">Level 1: setup, accumulator, addition</h2> <p>Here’s a repo: <a href="https://github.com/mmertsock/mountain-goat-vm"><code class="language-plaintext highlighter-rouge">mountain-goat-vm</code></a>. Named as such because I like mountains, goats are cool, and this will be like building a little mountain one stone at a time. I’ll post source code there and also host a copy on Glitch that’s ready to run in your browser.</p> <p>As a first step toward building an assembly language—which is the real meat of what I wanted to play with—we’ll just implement a simple <code class="language-plaintext highlighter-rouge">ADD</code> instruction. A lot of the initial work is not the machine itself but establishing the HTML/JS chrome to host this project. Input boxes and text areas. A simple <a href="https://en.wikipedia.org/wiki/Read–eval–print_loop">REPL</a> to execute instructions one at a time, and a “program loader” to execute a block of code. Here’s the definition for our <code class="language-plaintext highlighter-rouge">ADD</code> instruction (as produced by the HELP command in our REPL):</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ADD: Sets $R0 = $R0 + $R1 Example: ADD </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">$R0</code> indicates register number 0, etc. So our machine needs two registers (<em>only</em> two for now—only implementing what we need and nothing more—but surely we’ll add more registers later). Since there are only two registers and addition is <a href="https://en.wikipedia.org/wiki/Commutative_property">commutative</a>, our <code class="language-plaintext highlighter-rouge">ADD</code> instruction doesn’t actually need to be told which registers to add, and thus an <code class="language-plaintext highlighter-rouge">ADD</code> statement has no arguments. The result goes into $R0, which I guess means our machine is currently just an “accumulator”. Which is all it can do right now, no real logic, so that sounds right.</p> <p>Let’s assume the machine is initialized with all registers set to zero. It’s easy to see the <code class="language-plaintext highlighter-rouge">ADD</code> instruction alone <a href="https://en.wikipedia.org/wiki/Entropy_(information_theory)">produces no information</a>. So we’ll add one more instruction to set a register:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SET: Sets $Rn to integer value i Example: SET n i n: register [0 - 1] i: word [0 - 65535] </code></pre></div></div> <p>Now we have an adding machine. Feel free to use this to file your taxes this year.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SET 0 3 &gt; [3] [0] SET 1 4 &gt; [3] [4] ADD &gt; [7] [4] SET 1 2 &gt; [7] [2] ADD &gt; [9] [2] </code></pre></div></div> <p><a href="https://mmertsock-mountain-goat-vm.glitch.me/level-1.html">Try it out yourself in a browser on Glitch.</a></p> <h2 id="implementation-details">Implementation details</h2> <p>Full source code is on Github in <a href="https://github.com/mmertsock/mountain-goat-vm/tree/level-1">branch <code class="language-plaintext highlighter-rouge">level-1</code></a>.</p> <p><strong><a href="https://github.com/mmertsock/mountain-goat-vm/blob/level-1/assembly.js">assembly.js</a></strong> is where the computer itself is implemented.</p> <p>The <code class="language-plaintext highlighter-rouge">Machine</code> class represents an instance of the computer itself. It has registers that hold data, and simulates the lowest-level <a href="https://en.wikipedia.org/wiki/Machine_code">machine code</a> to manipulate individual registers. It’s rather passive right now, notably because we don’t yet have a need for a <a href="https://en.wikipedia.org/wiki/Program_counter">program counter</a> to track which instruction to execute next (e.g. for actual logic). For now, our web app just tells the machine which instructions to execute; the machine can get more agency later.</p> <p>The <code class="language-plaintext highlighter-rouge">DataType</code> and <code class="language-plaintext highlighter-rouge">Instruction</code> classes are all at a meta level: they define categories of data and behavior, not individual instances of instructions or data. They describe how the computer works and will form the basis of our assembly language, memory system, etc. For example, <code class="language-plaintext highlighter-rouge">addRegisters</code> is an instance of Instruction that describes how the ADD instruction works. <code class="language-plaintext highlighter-rouge">setRegister</code> is actually a factory method that produces versions of <code class="language-plaintext highlighter-rouge">SET</code> instructions for machines with different numbers of registers (to prevent propagating the assumption that our machine has only two registers, since that will certainly change).</p> <p>The <code class="language-plaintext highlighter-rouge">Program</code> and <code class="language-plaintext highlighter-rouge">REPL</code> classes do most of the heavy lifting right now, they parse assembly language text input, determine the Instructions each input statement represents, and tells the machine to execute those instructions using any parsed arguments. For now, we’re skipping the step of encoding these statements as binary machine code: it’s all operating symbolically, more like “Machine, execute an ADD instruction” rather than the Machine decoding and interpreting a value 0x0010 in some memory or register location as an ADD instruction. Currently these classes are directly driving the operation of Machine, but in future iterations the Machine will need to run itself with the Program and REPL acting more as clients or I/O interfaces for it.</p> <p><code class="language-plaintext highlighter-rouge">REPL</code> understands one additional keyword that isn’t an actual Instruction: <code class="language-plaintext highlighter-rouge">HELP</code>. It gathers and displays metadata from all of the actual Instructions that the Machine declares as supporting, for the benefit of the programmer.</p> <p><strong><a href="https://github.com/mmertsock/mountain-goat-vm/blob/level-1/index.html">index.html</a></strong> hosts the machine and all the HTML/CSS/JS chrome to display the inputs and outputs. It interacts with the <code class="language-plaintext highlighter-rouge">Program</code> and <code class="language-plaintext highlighter-rouge">REPL</code> classes above based on user input. Pretty simple.</p> <p>Note that so far, the virtual machine has no true interaction with the outside world. No memory, no I/O. Our web app is directly parsing and feeding it instructions to execute from user input, and directly inspecting the registers to show what happened. It would be like using microscopes or oscilloscopes pointed at a CPU and calling that the “output” of a real computer. At some point we’ll need concepts for memory and I/O.</p> <p><a href="https://iosdev.space/@mmertsock/109764956915503153">Ping me on Mastodon</a> to discuss.</p> <h2 id="whats-next">What’s next</h2> <p>Multiplication. This will require a program counter and some additional instructions to branch, jump, and decrement. The beginnings of some real computing.</p> Fri, 27 Jan 2023 00:00:00 +0000 https://www.runningcode.net/2023/01/27/mountain-goat-vm-1/ https://www.runningcode.net/2023/01/27/mountain-goat-vm-1/ Breath in the Wild <p>thinking about another playthrough of <em>Breath of the Wild</em>, with the emphasis on “wild”. as in, “in the wild”. the rules as planned, presented in bullet form because i’m a fiend for bullets:</p> <ul> <li>no* fast travel</li> <li>pro display mode</li> <li>the “Stable Rule”. no* use of the following outside of stables, towns, etc.: <ul> <li>the sheikah slate map</li> <li>cooked meals. elixirs and individual pieces of raw/toasted ingredients are ok</li> </ul> </li> </ul> <p>how will the travel and map restrictions affect the experience? hopefully more immersive within the world. really seeing the terrain. seems likely i’ll spend a lot more time on horseback. lots of travel and exploration may involve following roads, reading signs, talking to NPCs to get directions. or will i climb a mountain and set a bearing visually instead?</p> <p>and avoiding use of the map while out in the field reduces reliance on “gps”, keeps you in the moment and out of the menu system. but one would certainly expect to have a paper map available to link at any stable or town, along with an NPC to point out where you currently are. pro display mode reduces distracting and immersion-breaking UI chrome, and there’s no mini-map on screen constantly hinting your location.</p> <p>the idea of the Stable Rule is to eliminate some of the implausible actions (eating a large meal in the middle of combat) and rebalancing a bit in favor of realism in terms of food, health, and navigation. maybe nintendo should have had each food item consume some stamina, so if link eats 20 apples in the middle of a battle, they’re left vulnerable (tummy ache simulated by an empty stamina wheel). and yes, link’s magical infinite pouch is legendary, but let’s say that it’s not realistic to carry a fully cooked meal around for days, whereas it’s more sensible to pop some elixirs or ingredients on the go. maybe nintendo should have cooked food turn into Dubious Food after each blood moon!</p> <p>with the prospect of being marooned far from a safe space without a massive cache of healing items, elixirs should have much greater tactical value with their status effects (should be nice to actually use elixirs and monster parts, i feel like they’re not utilized strongly enough in typical gameplay). this means using more monster parts. but the more monsters you slay, the sooner they level up. interesting!</p> <p>* where “no” means “minimize it”. not going to throw my Switch in the trash if i break the rules once in a while :p</p> Thu, 05 Jan 2023 00:00:00 +0000 https://www.runningcode.net/2023/01/05/in-the-wild/ https://www.runningcode.net/2023/01/05/in-the-wild/ Twisted Branch 2022 <figure><img src="https://www.runningcode.net/assets/posts/twisted-coffee.jpg" alt="A Twisted Branch finisher's mug full of coffee, my race bib, and a phone showing this blog post including a rendering of this same image." style="max-width: calc(min(80%, 400px));" /></figure> <p>So I ran <a href="https://www.twistedbranchtrail.com">Twisted Branch 100K</a> again. <a href="https://www.runningcode.net/2015/09/07/twisted-branch/">Previously</a> had two finishes, one DNS, and a couple years of just hanging out around the event, pacing, etc. First time doing any sort of long race since 2018, and first time starting this race in six years. Signed up in January on a bit of a whim, wasn’t sure if I could survive the race anymore, let alone match what was very plausibly a long-past period of peak performance.</p> <p>But in the end, this race set the framework for an entire season of some of my best training and possibly the best fitness level ever. I finished the race with a new PR, and more importantly emerged with a much improved perspective on running and racing.</p> <h2 id="️️">♥️🍪♥️</h2> <p>First: thank you Valerie, crew extraordinaire, listener, coach. Unfailingly accepting of every silly adventure. Always knows exactly what I need to hear. Impossible to imagine this life without you as a partner.</p> <h2 id="inspiration">Inspiration</h2> <p>Strat and Jeff and Laura: my adventure partners over all these years. Back in my peak racing years of 2015–2016, was just getting to know them, and they were inspiration to do more and a catalyst for many other new experiences and friendships. Between then and now, an endless wellspring of hard, weird, easy, long, simple, creative, questionable adventures. Always ready to jump in on some oddball idea, always easygoing, no pressure, just acceptance. Memories that I now can draw on for any situation, knowing that a person can achieve anything (or can just take a break and relax).</p> <p>Abby M: just drops these mythical adventures and enormous feats out of nowhere. Couldn’t stop thinking about Abby’s performance at <a href="https://vacationwithoutacar.com/races/vol-state/">Vol State 500K</a>, and <a href="https://vacationwithoutacar.com/5-days-update/">Laz’s chronicles</a> of the race. Abby has this way of casually stepping into the unknown, marching through unimaginable hardships, and emerging often victorious but always transcendent and wiser. And then sharing the experience in an authentic, relatable, instructive way. But also clearly training hard, thoughtfully preparing for the day, and getting the most out of gear.</p> <p>Jamie H: keeps showing up year after year, constantly improving, training with method and precision, knowing exactly when to apply big volume, when to recover, how to cross-train.</p> <p>The new Rochester trail community: it’s a new generation since I was in the game. Don’t know many of the new people, but it’s clear from those I have met (e.g. Cruz and Clement) that it’s more positive and inclusive than ever. The future has hope.</p> <p>And in the moment of this race: I was trading places all day with a handful of runners; they were stronger on the hills and flats and I caught up on the descents. Their consistent work mile after mile gradually pulled me further out of the comfort zone over the day, rekindled the muscle memory of the Tactics of Racing, and finally helped me push beyond the boundary of what I thought would be possible.</p> <h2 id="method">Method</h2> <p>After 2015 (the first year of Twisted Branch, and overall what I feared was my peak of running and racing, forever out of reach), my racing performance lacked any consistency. A few decent results but a lot of DNS, DNF, or just lazy efforts. Much of this was clearly attitude: hubris, a lack of discipline, caring too much about the result or expectations without putting in the work to make it happen. Too relaxed in the wrong ways, not relaxed enough in the wrong ways.</p> <p>After taking a few years off of racing and just doing some low-key adventuring, I finally felt ready to try it again. Valerie accurately predicted that having a big goal race an entire year away would give some proper structure and motivation to training. It worked.</p> <p>In those years of failure, my training focused too much on hard numeric goals and endless escalation of volume. Small hiccups—missing a mileage goal, unexpected schedule disruptions, a single bad run—would easily send me spiraling and I would lose weeks of work at critical times. This year, with the benefit of self-awareness and a long time away from all that, I tried to avoid these mistakes.</p> <p>I don’t use a coach or have a grand plan for training. When ready I start a training cycle that lasts several weeks, execute it one week at a time, and then when it’s time to rest the cycle ends, and starts again after a couple of easy weeks. Each training cycle had some rough overall goals, not numeric, just concepts to improve on. Each Monday I assess the schedule and weather, assemble a rough plan, come up with a couple of specific key workouts, and choose some cumulative numbers to target for the week: distance, time, <a href="https://fellrnr.com/wiki/TRIMP">training impulse</a>, or elevation gain. Time and training impulse are useful metrics for cross-training to feel more worthwhile and not like wasted time.</p> <p>Many weeks don’t go as planned: a key workout is missed, a number isn’t quite reached, the schedule goes off the rails. Maybe I go way beyond the goal, or pivot to some different goals mid-week. When the week’s over I accept it as just one stone in the bucket of the training cycle and account for that result when planning the next week. No individual workout or week is a failure that will break an entire season. A great performance isn’t permission to stop and say that’s good enough, just move on to the next week and keep building.</p> <p>And—I’ve always believed this but now I consider it a central tenet essential to healthy training—every individual workout needs to have some sort of purpose. That purpose might be discovered or changed mid-workout, and it might be simple or banal (e.g. “just enjoying the sun”), but it has to be there. Rest or simply goofing off is better than junk miles.</p> <p>A lot of this happens to align with Jack Daniels’ training methods (recommend <a href="https://greenje91.codeberg.page/words/20220705-danielsrunning.html">Jeff Green’s thoughts</a>), for what it’s worth. Mostly coincidentally, though I’ve been vaguely familiar with it.</p> <p>The late winter/early spring training cycles were mostly focused on base building and gradually elevating aerobic capability, with lots of road mileage and interval workouts. I leaned heavily on heart rate training and steadily increased pace to stay in the HR zones as fitness increased; eventually this unlocked a faster “easy” pace than I had thought was appropriate in past years. Measured progress via VO<sub>2</sub> max and 10K/half marathon time trials (and PRs). One capstone effort was a five-day streak of 3+ hours running/cycling each day (across the full range of Rochester’s March weather), totaling 77 mi running, 37 mi biking, 15h total time.</p> <p>Spring and summer cycles added trail and hill work, lots of heat/humidity acclimatization, and occasional (but not too many) long runs or hikes. Plenty of trips to the mountains. Long mountain hikes and runs are my favorite recreational application of running so this broke up the monotony of just doing long runs every weekend. A big hike is a nice way to build experience with very long days, and have a massively shorter recovery time (and less risk of injury/overtraining) compared to extra-long training runs. I have lots of long-term goals in the Adirondacks, and constructing adventures to chase these goals was a nice way to build and test ultrarunning skills. This culminated in my two longest-ever ADK hikes, some <a href="https://www.runningcode.net/2022/06/12/adk-signs/">cool adventures</a>, <a href="https://www.strava.com/activities/7437814384">finishing the 46 high peaks</a>, and my biggest ever week in total climb (21,838 ft) and time (29h).</p> <p>As the race drew closer, I actually paid attention to hydration and calorie tactics: previous years of racing I either overdid it, or later thought I didn’t need much of anything. Lots of reading, experimenting, and testing led me to confirm I actually do prefer low intake. So I built a structure for the race which allowed for that but ensured there was a consistent baseline to stay out of the weeds: a single chewable and a sip of water every 15 minutes which amounts to 5–10 oz water and 60 calories per hour. Once per hour or so, larger intakes of food and water would add more bulk as needed, settling around 100 cal/hr for mountain hikes and 200 cal/hr for running, and water around 5–15 oz/hr depending on weather and effort. [This is not prescriptive, the difference in ideal intake from person to person can be massive. Just find what works for you, and actually test it before a race.]</p> <p>The final training cycle ended in “dress rehearsals” for Twisted Branch: <a href="https://www.strava.com/activities/7550357448/overview">back</a>-to-<a href="https://www.strava.com/activities/7555507956/overview">back</a> 50K runs on the race course, in the exact clothing/gear configuration for race day, aiming for the target race pace, following the race plan for water and calories. They went pretty smoothly, met the pace goal, validated the water/food plan, and sorted out a few mistakes and gear issues that could have really messed up the race. They gave me some assurance on race day that I had a solid plan for a PR.</p> <p>Overall, the idea was to eliminate as much guesswork and unnecessary decisions during the race as possible. Control all the variables that I could and avoid unforced errors, so I could focus my mental and physical energy on responding to the inevitable vagaries of the day that are not controllable.</p> <h2 id="trust">Trust</h2> <p>This year, and this race, after years of absence and fearing failure, was an exercise in trust. Learning to trust friends, that they really are up for spending long days doing silly things, and that it’s ok to ask for help. Even simple things like asking for a ride (thanks Laura!).</p> <p>In past years, I got through this race with luck and the brute force of youth. This year, I made a bet on experience and scientific method to maximize what’s left of any talent I had and avoid as many mistakes as possible. Methodically building fitness and refining strategy, and during the race trusting that that work would carry me through to the end.</p> <p>In the early miles of an ultra, it’s easy to look at what’s ahead and despair that you won’t be able to maintain goal pace for the whole day. Those dress rehearsal runs gave me some solid evidence to trust that the plan was going to work, to quiet the nerves and just keep moving.</p> <h2 id="letting-go">Letting Go</h2> <p>My past racing career ended with increasingly unrealistic internal expectations and fear of (perceived) external expectations. Comparing to others. Ultrasignup scores. Fear of letting others down. Getting hung up on missing smaller goals, which led to being unprepared for larger goals. Bad mindsets all around.</p> <p>This year was an exercise in letting go of all of that. After years of avoiding racing, and just adventuring with friends, finally felt ready for a fresh start. I had this dual, maybe dissonant mantra of “no expectations, nothing to lose”.</p> <p>I’d like to think that being self-reliant in the wilderness has cultivated a mindset that is much less dogged by the fear of failure. Not “failure is not an option”, more like “failure doesn’t exist as a concept in my brain”. On a long day in the wilderness, that literally is how you have to operate: the only thing you can and must do is get to the next summit, and then get home. There is no option to quit. I mean most days are not extreme, it’s not Antarctica, you can cut the route short, etc., but there is always some level of responsibility to take care of 100% of your own basic needs until it’s finished. The only way out is through.</p> <p>Mountain running and off-trail navigation, even more than trail running, puts one into moments of total focus and flow. The puzzle and art of moving quickly over mountains and through unbroken forest is to answer with every step “how best do we get to the next point” and “how do we perform the next move in this scramble”, not “can we even get up that entire thing” or “do we feel like doing some more miles”.</p> <p>So in the race, I tried to stay in that flow. Going mile to mile, aid station to aid station, focusing on what was required for that moment, letting go of what happened in the hours before or what may or may not unfold in the hours ahead. Breaking down that scary “can I finish this race” question into the simple tasks of “what’s the best line down this hill”, “can we jog a little more on this easy part”, “sweat rate is dropping, have an extra sip”.</p> <p>I see Twisted Branch as a play in three acts. Miles 0–21 are the warmup: keep it easy. 21–46 are the critical ultrarunning miles: avoid mistakes, keep grinding, don’t lose your way (figuratively or literally) in the mental purgatory between The Lab and Bud Valley, and have the strength and skill to handle the escalating effects of whatever (hot or wet) weather the day has to offer. And then 46 to the end is where you race.</p> <p>I leaned on all that planning to get to mile 46. Was still basically right on time to match and hopefully beat my best time. Body was still feeling strong. The main thing left to face was that fear of failure, the aversion to actually finishing a thing and doing it well, going beyond the easy and comfortable.</p> <p>I was fortunate to be keeping up with a few cool people all day, and now chasing them down would be the catalyst to push a little harder. The climbs and miles ticked by, and each one was a mini-race, an attempt to keep up with the goal pace, to try to figure out how to break that mental barrier of trying harder. Finally, the pieces fell into place: I relaxed, remembered to stay in the moment, solve the problem at hand, stop caring about the numbers, and just go. I finally let go of everything and just ran. <em>No expectations, nothing to lose.</em></p> <h2 id="the-end">The End</h2> <p>The final miles were a fun experience. Almost lingered too long at the last aid station; the hot coca cola and Valerie’s tough (and perfect) coaching were the slap in the face to just get moving and finish before the PR fell out of reach. Mount Washington was what it always is. Just climb. Then some unknown (I always forget) number of miles over a weird rolling ridge with your mind no longer functioning, and a long descent dumping straight into the finish line.</p> <p>For “reasons”, my watch only had time and heart rate metrics visible, and I was so close to the PR threshold I really had no idea if I could get it. I think this worked well actually to let go one final time, just run all out, <em>no expectations, nothing to lose</em>. Somewhere on the ridge I fell, knowing the legs were threatening to cramp, but somehow I willed them to keep moving. A little steep rough descent down a drainage, finally sort of knew where I was as it levels back out, smoother and more runnable than I remembered, turned up the pace again.</p> <p>You get a tease of the final downhill on some pavement (6:57 pace), then a stupid mean climb that everyone hates (ran almost the whole climb this year), then the final descent, increasingly steeper and rougher. Tumbling down the hill, bouncing off each switchback, tunnel vision, every step an eternity of focus to stay upright and find a survivable line through the loose shale and roots, only at the very end with the crowd audible and the finish visible through the trees did I finally know a PR was actually happening. Across the road, stumbling through the finish, crumpling onto Valerie. Immobile on a cot, finally my legs had permission to give up and cramp. And finally a properly cold drink. The sun was still up. Lots of cool people everywhere. A nice evening.</p> <h2 id="anyway">Anyway,</h2> <p>Good times. Party. Data on <a href="https://www.strava.com/activities/7675468701/overview">Strava</a>.</p> <p>Do you like gear? Here’s what I used for the race:</p> <ul> <li>Shoes: VJ MAXx. Somewhat overkill for this race (VJ Ultra would be more sensible for most people), but the Ultras were doing a weird thing with my ankle and the MAXx had proven themselves to be absolutely reliable through everything after many long days in the mountains. And surprisingly comfortable, even at the end of a 13 hour hike. It’s nice to literally not have to think about your shoes or feet at all, to unconditionally trust that they will carry you over anything, while stumbling down a rudely steep descent over roots, grass, and loose shale at mile 55.</li> <li>Smartwool crew socks (long sock club)</li> <li>Shorts: Rabbit Shredders. These things stay dry and lightweight through the wettest and nastiest sweatiest days, and have an incredible pocket system. Like I was carrying 800 calories, electrolyte tablets, and a buff, all instantly accessible, comfortably, with no bounce.</li> <li>Shirt: Rabbit tank. Light and cool. A big fan these days of tri-blend shirts—shop at <a href="https://beckhornprints.com/search?q=tri+blend">Beckhorn Prints</a> for the best designs—but I learned on that 50K dress rehearsal that the hottest long runs are maybe a little too much for them.</li> <li>Trucker hat from <a href="https://beckhornprints.com">Beckhorn Prints</a>. Well, three. No change of shoes or shirt during the race but a fresh dry hat &amp; Buff every 20 miles was a simple luxury.</li> <li>Petzl headlamp</li> <li>2Toms is essential. Try it between your toes, too.</li> <li>Black Diamond Distance 8 pack. Packs and vests are hard to figure out, everyone has different needs; the Distance packs are my favorite because they ride supremely comfortably, have a great front pocket system, and are super durable and versatile.</li> <li>In the pockets: calories and electrolytes, and a Buff, in shorts pockets (see above). Trash secured in the back shorts zipper pocket. One soft flask in the front vest pocket with one of those long straws. An S-Biner secured the straw to a loop on the pack at just the right position. Phone in a front pocket. Back compartment of the vest: a quart bag with backup gear (socks, spare shoelace [helplessly witnessed a shoelace break while pacing once, and vowed to never be without a spare shoelace ever since], tiny med kit, toilet supplies), and an extra water flask (only used it on one long segment between aid stations).</li> <li>The gear list for crew was hilariously long; what actually got used included some extra calories (Skratch gummies and Spring speednuts), electrolyte tablets and fluid concentrate, a dry towel, a wet towel, those spare hats &amp; Buffs, and headlamp handoff/return. Trekking poles were queued up for the last segment but I declined.</li> </ul> <p>Thanks.</p> Sun, 28 Aug 2022 00:00:00 +0000 https://www.runningcode.net/2022/08/28/twisted-branch-2k22/ https://www.runningcode.net/2022/08/28/twisted-branch-2k22/ Trail Signs <p><em>Trail Signs, a.k.a. wandering, a.k.a. my biggest ever day in the ADK high peaks.</em></p> <p>june 2022. on the trail at 4:57 am and the sun is already rising, with endless hours before sunset, not much of a plan, and endless miles of trails to explore. time for an improv session.</p> <!--more--> <p>decided lots of turns on the spot, based on a rough idea of a loop, some peaks to add to my June grid, trails i hadn’t seen yet, whatever else looked cool on the map. even used an actual paper map and shunned the gps, just to keep it mellow and analog. felt great all day except for some jelly legs on that looong descent from gothics to JBL. i even stopped for a 45 minute break at JBL to lay in the sun, dry my feet, and top off the watch battery. also the JBL caretakers had free ham for everybody??</p> <p>decided to take a picture of every single trail intersection sign i encountered, because improvising turns at intersections was the game for the day and i quite enjoy trail signs. there were 48 of them! here you go:</p> <div class="linear-gallery"> <ol> <li><a href="https://www.runningcode.net/assets/posts/sign-grid-1.jpg"><img src="https://www.runningcode.net/assets/posts/sign-grid-med-1.jpg" /></a></li> <li><a href="https://www.runningcode.net/assets/posts/sign-grid-2.jpg"><img src="https://www.runningcode.net/assets/posts/sign-grid-med-2.jpg" /></a></li> <li><a href="https://www.runningcode.net/assets/posts/sign-grid-3.jpg"><img src="https://www.runningcode.net/assets/posts/sign-grid-med-3.jpg" /></a></li> <li><a href="https://www.runningcode.net/assets/posts/sign-grid-4.jpg"><img src="https://www.runningcode.net/assets/posts/sign-grid-med-4.jpg" /></a></li> </ol> </div> <p>the route (*asterisks mark stuff that’s new to me):<br /> rooster comb trailhead, across shoulder of snow to AMR via W.A. White trail* (wanted to see if that was a reasonable connection for some future plans), west river trail, cathedral rocks* (cool!), beaver meadow falls, beaver meadow area*, rainbow falls* (wow), weld trail* to the sawteeth col, sawteeth out-and-back, gothics, down the cables* (forgot they were there! fun!) to the col with saddleback, ore bed trail* (i love a rugged wild trail), short job*, klondike trail* and up yard* (lol), over the ridge* to big slide, down the brothers, garden, down the street to rooster comb inn.</p> <p>more details on <a href="https://www.strava.com/activities/7299777582">strava</a>.</p> <div id="linear-gallery-full-size"><img src="#" /><controls><a href="#">⟪</a><a href="#">close</a><a href="#">⟫</a></controls></div> <style type="text/css"> .linear-gallery { color-scheme: dark; margin-top: 0; margin-bottom: 0; /* cover the margins around 600px-wide `main`, plus 20px extra */ margin-left: min(-20px, calc(280px - 50vw)); margin-right: min(-20px, calc(280px - 50vw)); padding: 40px 0; background-color: var(--background-color-subtle); color: var(--text-color-main); } .linear-gallery > ol { list-style-type: none; display: block; height: min(65vh, 600px); margin: 0; padding: 0; overflow-x: scroll; overflow-y: hidden; white-space: nowrap; text-align: center; } .linear-gallery > ol::before, .linear-gallery > ol::after { content: ""; position: absolute; z-index: 100; width: 11px; height: min(65vh, 600px); box-shadow: 0 0 8px 10px var(--background-color-subtle); background-color: var(--background-color-subtle); } .linear-gallery > ol::before { left: -12px; } .linear-gallery > ol::after { right: -12px; } .linear-gallery > ol li { list-style-type: none; display: inline-block; height: min(65vh, 600px); margin: 0 10px; padding: 0; } .linear-gallery > ol li:first-child { padding-left: 24px; } .linear-gallery > ol li:last-child { padding-right: 24px; } .linear-gallery a:link, .linear-gallery a:visited, .linear-gallery a:active, .linear-gallery a:hover, #linear-gallery-full-size a:link, #linear-gallery-full-size a:visited, #linear-gallery-full-size a:active, #linear-gallery-full-size a:hover { text-decoration: none; color: var(--text-color-main); } .linear-gallery > ol li img { height: min(65vh, 600px); } .linear-gallery controls, #linear-gallery-full-size controls { display: block; text-align: center; padding: 0; } .linear-gallery controls { margin: 10px auto 0 auto; } .linear-gallery controls a, #linear-gallery-full-size controls a { margin: 0 3px; padding: 2px 6px; border-radius: 5px; border: 1px solid var(--text-color-main); } .linear-gallery controls a { font-size: 85%; } #linear-gallery-full-size controls { margin: 0 auto 10px auto; } .linear-gallery controls a:hover, #linear-gallery-full-size controls a:hover { background-color: var(--text-color-main); } #linear-gallery-full-size { display: none; } #linear-gallery-full-size.on { color-scheme: dark; display: block; position: fixed; top: 0; right: 0; bottom: 0; left: 0; margin: auto; padding: 20px; text-align: center; background-color: var(--background-color-subtle); color: var(--text-color-main); } #linear-gallery-full-size img { max-width: 100%; max-height: 100%; cursor: pointer; } #linear-gallery-full-size a.disabled { opacity: 0; } </style> <script> class LinearGalleryImage { constructor(a, index, gallery) { this.a = a; this.index = index; this.gallery = gallery; this.makePicker(this.a); } get fullSizeURL() { return this.a.href; } makePicker(a) { a.addEventListener("click", evt => { evt.preventDefault(); this.fullSize(); }); } fullSize() { FullSizeGalleryContainer.shared.show(this); } get hasNext() { return !!this.gallery.imageAt(this.index + 1); } get hasPrevious() { return !!this.gallery.imageAt(this.index - 1); } showNext() { let image = this.gallery.imageAt(this.index + 1); if (!image) { return; } FullSizeGalleryContainer.shared.show(image); } showPrevious() { let image = this.gallery.imageAt(this.index - 1); if (!image) { return; } FullSizeGalleryContainer.shared.show(image); } } class FullSizeGalleryContainer { constructor(root) { this.root = root; this.image = null; this.previousButton = this.root.querySelector("controls a:nth-child(1)"); this.closeButton = this.root.querySelector("controls a:nth-child(2)"); this.nextButton = this.root.querySelector("controls a:nth-child(3)"); this.previousButton.addEventListener("click", evt => { evt.preventDefault(); if (this.image) { this.image.showPrevious(); } }); this.closeButton.addEventListener("click", evt => { evt.preventDefault(); this.hide(); }); this.nextButton.addEventListener("click", evt => { evt.preventDefault(); if (this.image) { this.image.showNext(); } }); document.addEventListener("keyup", evt => this.navigate(evt)); } get inlineModalSupported() { return window.screen.width >= 700 && window.screen.height >= 700; } show(newImage) { if (!this.inlineModalSupported) { location.href = newImage.fullSizeURL; return; } let previous = this.root.querySelector("img"); if (!!previous && previous.src != newImage.fullSizeURL) { previous.remove(); } this.image = newImage; let img = document.createElement("img"); img.src = this.image.fullSizeURL; this.root.append(img); this.previousButton.classList.toggle("disabled", !this.image.hasPrevious); this.nextButton.classList.toggle("disabled", !this.image.hasNext); this.root.classList.toggle("on", true); img.addEventListener("click", evt => { evt.preventDefault(); this.hide(); }); } hide() { this.root.classList.toggle("on", false); this.image = null; } navigate(evt) { if (!this.image) { return; } switch (evt.key) { case "Left": case "ArrowLeft": case "Up": case "ArrowUp": evt.preventDefault(); this.image.showPrevious(); return; case "Right": case "ArrowRight": case "Down": case "ArrowDown": evt.preventDefault(); this.image.showNext(); return; case "Esc": case "Escape": evt.preventDefault(); this.hide(); return; default: break; } } } class LinearGallery { static buildAll() { if (Array.isArray(LinearGallery.galleries)) { return; } LinearGallery.galleries = Array.from(document.querySelectorAll(".linear-gallery")).map(root => new LinearGallery(root)); if (!FullSizeGalleryContainer.shared && (LinearGallery.galleries.length > 0)) { FullSizeGalleryContainer.shared = new FullSizeGalleryContainer(document.querySelector("#linear-gallery-full-size")); } } constructor(root) { this.root = root; this.images = Array.from(root.querySelectorAll("ol > li > a")).map((a, index) => new LinearGalleryImage(a, index, this)); let controls = document.createElement("controls"); this.images.forEach((img, index) => { let picker = document.createElement("a"); picker.href = "#"; picker.innerText = `${index + 1}`; img.makePicker(picker); controls.append(picker); }); this.root.append(controls); } imageAt(index) { if (index < 0 || index >= this.images.length) { return null; } return this.images[index]; } } LinearGallery.buildAll(); </script> Sun, 12 Jun 2022 00:00:00 +0000 https://www.runningcode.net/2022/06/12/adk-signs/ https://www.runningcode.net/2022/06/12/adk-signs/ Hyrule Warriors: Age of Calamity mini Review <p>I’m 100 hours into <em>Hyrule Warriors: Age of Calamity</em> (AoC). I’m having fun! I just finished the main quest and DLC quests, and have over 90% completion overall. Let’s discuss.</p> <p>This review has a single unequivocal assertion: if you haven’t played (and ideally completed) <em>Breath of the Wild</em> (BOTW), stop now. Close your browser tab, go finish that game, and return here a few months older to think about <em>Age of Calamity</em>. It’s not that you can’t play AoC without finishing BOTW, but why would you? AoC is very much a backstory, behind-the-scenes follow-up to BOTW. It’s not like the Star Wars prequels where any viewing order is valid.</p> <p>Actually nevermind, I’m incapable of unwavering opinions and shouldn’t gatekeep anyway. You’ll certainly miss some details and depth in AoC skipping BOTW, but we all must do what is best for our lives.</p> <p class="triforce"><span>🗡</span></p> <p>Anyway,</p> <p>This game’s for you if you always wanted to cosplay the flashback scenes from <em>Breath of the Wild</em>, especially this one:</p> <figure> <a href="https://www.runningcode.net/assets/posts/botw-memory-eldin.jpg"><img src="https://www.runningcode.net/assets/posts/botw-memory-eldin.jpg" alt="Screenshot of a flashback sceen from Breath of the Wild, showing Zelda and Link looking out at a field absolutely overflowing with recently slain monsters" /></a> <figcaption>INTENSIFY SCALE</figcaption> </figure> <p>It’s sort of like STAR TREK (2009), in a good way? A flashy, noisy, action-heavy diversion in an established franchise, exploring and breaking the canon in weird ways. The more-content-is-better philosophy. The plot may not be totally coherent <em>per se</em>, but it’s good entertainment, and [leaving the Star Trek comparison behind] I quite enjoyed spending as much time as possible with the large cast of characters.</p> <p>If you enjoy grinding, completing small tasks and side quests, optimizing and strategizing equipment, this game’s for you. This game is much bigger than I expected, and you can spend many hours diving deep. If you have a partner or roommate, make sure to check in occasionally to ensure the main menu music isn’t driving them mad—it’s pretty good music, but bystanders will hear a lot of it, especially if you play for completion.</p> <p>I like the soundtrack. Some of it is pretty good, and the tone is appropriate.</p> <p>Should you get the DLC? 🤷🏼. It’s not intended for the early stages of the game, so get to know it a bit and decide whether you definitely want more (I personally found the purchase worthwhile). It’s basically just more of the same stuff…more quests, more characters, more weapons, more cutscenes, no fundamental changes.</p> <p>Casual players would certainly have a different experience with <em>Age of Calamity</em>. The combat mechanics are complex and it’s hard to imagine <em>not</em> spending some time improving your weapons or completing at least the essential side quests. However, though I haven’t really tried it, I think the game could offer a good experience for casual players who just want to see the story and bonk some bokoblins. The easy end of a wide range of difficulty settings is there if desired. Two-player mode is another interesting option for casual players: you can participate while letting your partner do the heavy lifting. I did try this and it worked well.</p> <p>Or, if you like the Zelda franchise but really don’t want to invest the time in AoC, maybe find someone who has completed it and just watch all of the cutscenes.</p> <p>Speaking of game mechanics: it took me most of those 100 hours to finally slow down, mash the buttons a little less frantically, and take a more mindful approach. The combat controls have a certain rhythm, and if you take the time to feel it out, you can get into a nice flow state and play more efficiently.</p> <p>But anyway, tl;dr play this game if you’d like to perform a ballet with a lynel:</p> <blockquote class="twitter-tweet"><p lang="fr" dir="ltr">Pas de deux with Lynel<a href="https://twitter.com/hashtag/HyruleWarriors?src=hash&amp;ref_src=twsrc%5Etfw">#HyruleWarriors</a> <a href="https://twitter.com/hashtag/AgeofCalamity?src=hash&amp;ref_src=twsrc%5Etfw">#AgeofCalamity</a> <a href="https://twitter.com/hashtag/Zelda?src=hash&amp;ref_src=twsrc%5Etfw">#Zelda</a> <a href="https://twitter.com/hashtag/NintendoSwitch?src=hash&amp;ref_src=twsrc%5Etfw">#NintendoSwitch</a> <a href="https://t.co/eeNgrVpmPl">pic.twitter.com/eeNgrVpmPl</a></p>&mdash; mike mertsock (@mmertsock) <a href="https://twitter.com/mmertsock/status/1481436265983610885?ref_src=twsrc%5Etfw">January 13, 2022</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p class="triforce"><span>🗡</span></p> Wed, 12 Jan 2022 00:00:00 +0000 https://www.runningcode.net/2022/01/12/hwaoc/ https://www.runningcode.net/2022/01/12/hwaoc/ Dix Remix <p>Revisited the Dix range on a perfect sunny weekday in mid-August. My first visit was approached from route 73; this time I started at Elk Lake to see it from the other direction. Had a rough idea of a route in mind, some specific things I wanted to see, but largely improvised the details, deciding in the moment where to turn at each intersection. Mainly was just looking forward to a full day of baking in the sun up at high elevation and doing lots of climbing. The day did not disappoint.</p> <ul> <li>Who: solo trip</li> <li>What: Mountain running, hiking, and slide climbing</li> <li>Where: Dix Range, Adirondack high peaks</li> <li>Some numbers: five summits, just under 9 hours, 22.6 miles, 7,000 feet of climb, all of the sunshine</li> <li>Data: <a href="https://www.strava.com/activities/5804840912">Strava</a></li> </ul> <p>From Elk Lake—well, from any approach to the Dix range—the day starts with a few long quiet miles of pretty flat, quiet trail. Add a couple of miles of dirt road from the overflow parking because the trailhead lot was already full: a pleasant morning jog to loosen up. Once on trail, the grassy flats near the lake gradually, almost imperceptibly give way to steeper grades, and then inevitably, it shifts all at once at the fall line to that moderate long grind of mid-elevation climbing. That pleasant, only mildly painful, quad pump that can last for an hour or more.</p> <figure> <img src="https://www.runningcode.net/assets/posts/dix-remix-fall-line.png" alt="Elevation graph of part of the run, with a red circle indicating where the climbing grade suddenly shifts from moderate to steep, and a second red circle where the grade somehow gets incredibly steep." /> <figcaption>Red circle on the left: where the pain begins. Red circle on the right: where the pain gets silly.</figcaption> </figure> <p>I still make the mistake of not topping off water at the bottom of a climb. It’s so common to find approach trails that parallel brooks, going a long ways within earshot of water, and I think I’ll have easy access for a while so I may as well wait. But inevitably the trail rises way up above the flume and the only good water access is back at the start of the climb. However, this time I was lucky to find a small flow of decent water crossing the trail immediately before the Macomb slide, so I honored my good fortune by topping off both of my bottles, having a snack, and a long drink of extra water before beginning the slide climb. (Gear note: I carry a Katadyn BeFree filter attached to a soft flask for filtering and backup water storage. It’s one of the best investments you can make for lightweight backcountry hiking/running.)</p> <p>This was my first time on the Macomb slide, and one of the main attractions I wanted to see this day. Fun and quite a workout! All the loose rock and dirt seemed rather different than the other slides I’ve seen. Why is that? Is that what newer slides look like?</p> <p>Over to Carson, had a blast on the scramble. That rock is so sticky. And the alpine area near the summit is interesting. Can’t quite explain why, but Carson reminds me of the White Mountains.</p> <p>Next was Grace. But I switched it up a little, and descended the herd path just before the summit, then turned around and climbed the Great Slide. During my first time on the Dix range, we exited the range by descending this herd path, and was quite intrigued by the slide, and finally after nearly four years I got to climb it. Lots of fun. I found some simple beta on <a href="https://www.summitpost.org/great-slide-of-east-dix/160564">SummitPost</a> that was all I needed to know: start right, move left through the middle, finish right. Found blueberries on the slide, the most scenic fruit I’ve ever seen.</p> <p>Back to Carson. Decided to check out the side trail that connects the col between Carson and the Lillian Brook path. As a bonus, I got to go back down the cool rock scramble, and add on some extra climb for the day. Was surprised to find a source of clean cool water flowing out of the earth just a couple of minutes down this side trail from the col—totally worth keeping in mind if you’re thirsty on that side of the range, though I don’t know how reliable it would be. This trail was a little wild, a bit of an August jungle, moving in and out of that drainage (presumably the main source of Lillian Brook), steep but not too step, a nice midday steam with the sun on my back.</p> <p>Reached the main branch of the Lillian Brook trail after dropping about 800–900 ft elevation from Carson, and now I turn right to climb back up to the col beneath Hough. That is a quick but steep and relentless climb, your face in the dirt and roots. And then you’re back on the ridge trail, immediately continuing up Hough with no break. Did some sweating there.</p> <p>Next over to the Beckhorn. I had never seen that or the Dix summit on a clear day so I was looking forward to this. It did not disappoint. Didn’t realize, or perhaps had forgotten, how narrow that ridgeline gets. Even covered in trees, its shape is pretty clear. Nice views of the Grace slide, though it’s funny how dramatically the apparent grade of a slide can change depending on where you’re viewing it from.</p> <p>Quick hop over to the Dix summit. And then decided to finish the day by descending the Beckhorn trail, also new to me. The idea being with one more visit using the Hunters Pass and Lillian trails, I can finish redlining all of the (former) Dix wilderness. Stopped on the way back to the Beckhorn to watch what I believe were peregrine falcons soaring around it.</p> <p>The Beckhorn trail was super fun to descend. The exposed parts at the top were a little hair-raising with a long day’s fatigue, and the middle parts a pretty unique and scenic (only mildly technical) jog. Then you know the rest, a long march back to the trailhead. The cascades at the Lillian Brook crossing made for a great spot to break up the return trip and record some video, have a snack with dirt-soaked hands, and think about the day’s events.</p> <p>Overall a perfect day in the woods, 10/10. Felt great all day. Not great as in it was easy, but that I was actually putting in real work moving fast on steep rock, performing well, taking care of calories and hydration, good energy levels, and really dialed in my gear configuration. But most importantly, it was a good day of living in the moment and feeling that visceral connection to the land that I only find in mountain running. I hope we all can find something like that in our life, whatever works for each of us, that we can return to often.</p> Mon, 30 Aug 2021 00:00:00 +0000 https://www.runningcode.net/2021/08/30/dix/ https://www.runningcode.net/2021/08/30/dix/ The Official Old Mountain Coffee® Giant Wilderness Traverse™ <p>In late July, Strat and I did an extended version of the classic Rocky Peak-Giant traversal: starting at the east trailhead in New Russia, traversing Rocky Peak Ridge to Giant, but then instead of descending directly to 73, taking the north trail off the “back” of Giant deeper into the wilderness near Green, and then over Hopkins and Spread Eagle before finishing right in Keene Valley. Or more precisely, finishing at the door of Old Mountain Coffee for post-hike treats.</p> <p>This was a very fun, mellow, quiet trip! We had both seen all of these summits, but had not seen the east approach (except Strat, in the winter), nor any of the trails connecting Giant to Hopkins, nor the descent from Spread Eagle to the Beede Road neighborhood. So, lots of new experiences punctuated by visits to familiar peaks. And we had the whole place to ourselves: only saw a single other party on trails the whole day.</p> <p>It was a very damp and unseasonably cool morning following a long day of rain, so the whole first half was walking through magical rainforests of massive old-growth trees, or shivering inside clouds along the open ridge lines. We could swear some of that fine mist was snow. But endless ripe blueberries, interesting stunted oak trees, the steady trickle of fun little rock scrambles, and plenty of conversation kept us moving. We both get cold easily and are very stubborn about digging warm layers out of our packs, so that’s just how we do things.</p> <p>We misremembered the exact sequence of the little peaks and features along that ridge traversal, so the middle part felt endless, and then unexpectedly we found ourselves on Rocky Peak. Felt good to be on one of the few parts of trail we knew just as a bit of sun came out to warm us up, and we made excellent time getting over to Giant.</p> <p>Next was the descent along the north trail toward Green. We had talked about maybe bushwhacking up Green to work on our hundred-highest log, but I had an excellent rationalization for our cold tired bodies to save this for another day: a full east-to-west traversal of Green starting in the brook near Owl’s Head sounded way more interesting. I had read there were cool rock formations, why miss out on that?</p> <p>Anyway, the north trail was a treat. Always interesting to see how different these lesser-used trails can feel. This was a primeval area full of green mosses and ferns, gray mist, and the rich reds and browns of rotting wood. All of these colors were even more <a href="https://alifespentwondering.com/a/82/why-are-colors-so-vibrant-after-a-rainstorm/">saturated</a> after the rain. It felt like moving through a waking dream or some fantasy film.</p> <p>On the way to Hopkins, the trail goes over a bump on the shoulder of Green through a rather shadowy stretch of spruces. To add to the spooky mood, we found lots of ghost pipes growing at the edges of the trail. So I named this Ghost Pipe Mountain.</p> <figure> <a href="https://youtu.be/61-o2YaECno"><img src="https://www.runningcode.net/assets/posts/giant-2021-yt-thumb.jpg" /></a> <figcaption>Here’s a <a href="https://youtu.be/61-o2YaECno">video</a> on YouTube</figcaption> </figure> <p>The clouds finally broke just in time for our arrival at the Hopkins summit, and we savored dry rock and spectacular views. It’s one of our favorite summit views. Then time to finish up, we were ready for snacks so we hurried down into town. We had started the day with a stop at Old Mountain to fuel up before the hike, and now were finishing it there, reporting the day’s exploits to Nick—the same barista who saw us off in the morning.</p> <p>Good times and a fun route. I would revisit this in every season to see how the terrain, views, and effort levels change in various conditions.</p> <p>Here’s some <a href="https://www.strava.com/activities/5712517161">Strava</a> data.</p> Sat, 31 Jul 2021 00:00:00 +0000 https://www.runningcode.net/2021/07/31/giant-wilderness/ https://www.runningcode.net/2021/07/31/giant-wilderness/ Snowy and Lewey Mountains <p>A nice pair of Adirondack hikes in May 2021. My first mountains this year — now that me and my friend Strat are fully vaccinated — and a warm-up for the season while working toward my goal to visit the ADK Hundred Highest. Both of these summits were new to us, as we had only seen a few of the hundred highest outside of the 46 high peaks. We both enjoy avoiding crowds and off-trail adventures, so we’re really looking forward to seeing more of the “other 54”.</p> <h2 id="snowy-mountain-friday-evening">Snowy Mountain, Friday evening</h2> <p>Arrived at the Snowy trailhead around 5:30 pm. Just one other vehicle in the parking area. A very quiet evening with calm mild weather; just a cloud of flies to keep us company as we prepared. My pack was pretty heavy with assorted gear, not knowing whether to expect cold/snowy/wet conditions, and also it always takes a couple hikes each spring to relearn efficient packing strategy.</p> <p>The lower parts of the Snowy trail were quick and pleasant. Looks like the trail had some improvements in recent years? The dry easy footing afforded us more attention to all the spring wildflowers (trout lilies, dutchman’s breeches, etc.), the open woods, and eventually a rather nice brook. We passed one other party coming down the mountain, and that was our only human contact for the entire weekend of hiking.</p> <p>The main climb to Snowy’s summit was pretty much the steep eroded workout I expected and enjoy, but the snow and ice I feared based on reports from the high peaks region never materialized, except for a few tiny patches tucked in a corner on the steepest part of the climb (Strat didn’t even notice it on the climb, and couldn’t believe it when he spotted it on the way down). I gave it a little poke and moved on.</p> <p>Cool summit and a cute little fire tower with incredible views. Thought it was interesting that the observer cabin was a bit distant and downhill from the tower, with basically no clearing around the tower itself: it was awash in trees right up to the foundation. Trees which thoughtfully caught Strat’s hat after it blew off of his head up in the windy tower cab.</p> <p>Hopped back down and finished the hike at dusk, making a game of getting back to the trailhead before headlamps were required (which is getting harder every year as our night vision weakens). Reminisced in the near-dark about various weird wildlife sounds and smelling bears during previous bushwhacking adventures.</p> <figure> <a href="https://www.runningcode.net/assets/posts/snowy-dusk.jpg"><img src="https://www.runningcode.net/assets/posts/snowy-dusk.jpg" /></a> <figcaption>Checking out the view on Snowy at the old observer cabin clearing as dusk approaches</figcaption> </figure> <h2 id="lewey-mountain-saturday-morning">Lewey Mountain, Saturday morning</h2> <p>Next morning we slept in a bit, got coffee at Stewart’s, and arrived at the Sucker Brook trailhead at 7 am to climb Lewey Mountain. Perhaps I misread the guidebook, as I had the impression that the Sucker Brook trail would be lightly maintained and tricky to follow — more like a herd path — but it was a beautiful, perfect piece of singletrack all the way to the col below Lewey. A soft duff surface under towering pine trees with several very freshly cut logs as evidence of maintenance. Totally dry conditions and easy rock-hopping at the stream crossings. [ed: <a href="https://www.adkhighpeaks.com/forums/forum/hiking/adirondack-trip-reports/512435-snowy-and-lewey-mountains-5-14-21-and-5-15-21?p=512447#post512447">credit to the Albany Chapter of ADK club</a> for recent maintenance.]</p> <p>We found the height of land marking Lewey’s east ridge and stepped off the trail to begin our climb. Made note of a twisty tree trunk to see if we could find this spot on the return trip. The climb started gently in wide-open woods, then the grade notched up in steepness every few minutes. Soon we were swimming through spruce, practicing our limp-arms technique to just phase through the limbs instead of fighting them. We found what we swore was a herd path, and with it we were able to make excellent time through much of much of the thick stuff all the way to the summit. The usual series of false summits and fun little ledges, but overall a really friendly bushwhack.</p> <p>We were nicely warmed up now with the morning sun at our backs all along this westward trajectory, and now overhead in clear blue skies. We ate some sour patch kids, drank some water, and shed some layers. Next step was to set a course for the descent. We checked the map and compass and calibrated our shadows so we could navigate by the sun.</p> <figure> <a href="https://youtu.be/XPplBu2fkGE"><img src="https://www.runningcode.net/assets/posts/lewey-yt-thumb.jpg" /></a> <figcaption>Hey I put a <a href="https://youtu.be/XPplBu2fkGE">video</a> on YouTube</figcaption> </figure> <p>The original descent plan was to follow the west ridge; the cirque between the ridges was also an option but I had wondered if that would be wetter due to mud season. But now it seemed pretty clear that mud would not be a concern today. So as we angled toward the west ridge and it was evident it would be a longish walk through some pretty thick trees, we allowed the slope to push us south toward the cirque. After riding out a ledge that was too tall to hop, we continued downward and very soon were in more open woods. The west and east ridges were visible above to either side, and also the summit behind us. Hard to ask for easier navigation.</p> <p>We decided to explore the cirque route since we could easily return to the ridge if things did get wet. But that never happened. Just easy going through increasingly open woods, even did some running. We encountered and stepped across several drainages that collected to form a small stream, staying on the right bank of everything to keep closer to the west ridge. We recognized various plants that belong in wet areas, and the ground had the appearance of getting soft and muddy from time to time, but today was a dry day.</p> <p>We were moving so fast we began to fear that we would blast right by the Sucker Brook trail. Not a big concern, since the trail parallels Sucker Brook proper and there’s no way we could miss that (and worst case, just going downhill/following all water downstream should guarantee you will return to the road). About 30 seconds after this discussion, we found the trail and it was clear as day. Turned left and headed back to the trailhead, and found the twisty tree exactly where we left it. We didn’t go through much of our water but the streams looked so clear and cold that I couldn’t help filtering a bottlefull for the road.</p> <p>Wrapped up the morning with pizza and ice cream in Speculator, airing out our spruce-scratched legs in the sun (sorry, passersby). A great start to the summer hiking season!</p> Tue, 25 May 2021 00:00:00 +0000 https://www.runningcode.net/2021/05/25/snowy-lewey/ https://www.runningcode.net/2021/05/25/snowy-lewey/ Palmers Pond 50K. aka Palmer’s Creek. aka LOL. aka Kid in a Candy Store. <p>great vibe at the starting line. something you can’t put a price on. most remote exit off a rural interstate, drive up a dirt road to the top of a hill, find the tents, pickups, and camp fire, that’s the race. by the time i had finished the race, you could still count on your fingers the number of cars that had passed the site. colin the nervous new RD. most people there knew each other, and would all get to know each other better. colin’s directions were thorough and amusing. discussion about who would clear the cobwebs, and whether todd beverly, holder of the #TrailsRoc golden axe, would clean up the trail for us.</p> <p>was chilly at the start, me being one of the least dressed of the runners: short shorts, tech shirt + wool shirt, light gloves, cap. was ready to run it hard though, knowing full well that a 50-miler practice run (one of my original plans for the day) was off the table because i can’t resist race mode. gear just a handheld bottle, a gel stashed in one pocket, a picky bar in the other pocket. inched toward the front of the crowd.</p> <p>looked back to say bye to Valerie, and with a word we started (i love these low key trail races where it seems everybody just starts running with no horn or gun or other hoopla). i took off in front with eric, he wasn’t racing but we would share a couple miles on the first lap, with super-trail-dog picasso excitedly showing us the way. i probably owe a lot of energy to starting the race with those two.</p> <p>pretty much instantly we all realized when Colin said this was the “dry” loop, this was strictly relative to the other loop that we would see in five miles. but it didnt really surprise me, and i actually thought conditions were pretty decent so far, given the terrain and recent weather and time of year. long rolling hills. lots of wildlife.</p> <p>ok, here’s what i really want to talk about. this place felt alive. <strong>alive</strong>. no, really, <em>alive</em>. wildlife, wild hills, wild trees, thorny plants defiantly growing in the middle of the trail. you could see and smell and <em>feel</em> the earth waking up from winter: ground softening and drinking fresh water to saturation, running water coming out of nowhere and traveling down and across the trail. some nice stream crossings. pools of clean clear water on and around the trail, full of frogs eggs. moss, spring shoots, colonies of ramps, tunnels of bright green conifers with soft red carpets of needles beneath. skinny whiplike branches of new growth reaching across the trail at eye level, tall old trees making us feel small, soft rotting wood returning to the earth. more sounds and evidence of wildlife than rochester-area trails; i flushed a bunch of grouse and a flock of turkeys. witnessing the awkward wonder of a turkey flying a couple hundred yards straight down the trail. the power and unpredictability of weather, past and present, making itself known: impressive blowdown, trees struck by lightning, uprooted trees forming huge earthen walls, warm sunlight, cold wind, rain, snow, sleet, hail. this land would not be tamed.</p> <p>at the aid station, Valerie asked, are you bleeding?, but it was just muddy water dripping down my legs: the blood of the earth. on the first lap i tore down dead branches to clear the way for other runners, in exchange young briars tore at my legs; there was balance in everything here.</p> <p>yes, most of the trail was atv doubletrack, but the earth had its way with even the trail itself, throwing it up long rolling climbs, pitching the path into odd cambers, adding ruts and heaves and brush and limbs and runoff and washouts and mud pits and vernal pools (and veritable lakes), deep shadow and harsh exposure, and forcing us off-trail to bushwhack careening through rough thorny woods with living obstacles thrust into our path at every concievable angle. these were not groomed recreational hiking trails. i had the sense that most of the humans who used these trails came here to hunt, to feed their families directly from the forest. but humans did not and could not own this land.</p> <p>after the second loop (ten miles in), i took off my gloves and wool shirt because i was warmed up and the forecast called for gently increasing temperatures. two miles later, i was in the middle of a blizzard, visibility down to nothing, bare hands clasped (where footing was good enough that i could do this without dying) under my shirt behind my back to stay warm and running even faster to stay warm. this was where i really started to give in to the call of nature and live in the moment. i just laughed at the situation because what else could one do? crashing blindly downhill with sleet stinging my face, rocks and mud under my feet and brambles whipping my legs, i hollered out loud and the wind laughed with me: i was having an absolute blast. this became the theme of the day: the race would pull out some new unexpected absurdity of a challenge, and i would just laugh and dive in. colin said i was smiling the whole time, “like a kid in a candy store”.</p> <p>to thrive in this place, to run this race, was to accept the power of nature and let it into your body and mind. to live in the moment and not just survive but honor and enjoy what we were given. and i did find joy, peace, and a sense of belonging in that forest, a sense of being alive, to a depth that i almost never experience. after the first couple of miles, i ran alone for the entire race, but i wasn’t alone–the presence of the forest was so real. it told me stories as i ran, it asked me to listen when i walked, it asked me to see when i stopped, it offered water and demanded blood, its soil mingled with our flesh.</p> <p>this race stripped us bare of our protective shell of civilized life, left us exposed to nature, our emotions, the strength and the limitations of our bodies. each time i crossed paths with a runner, i feel like we spoke to and understood each other not (just) with words, but in a more direct way, the original language of wild things that is in all of us but lays suppressed by the abstractions of culture.</p> <p>i would love to visit this same course in every season. i picture snowshoes in the cold deep silence of winter; steamy hot green summer; a gorgeous breezy fall display.</p> <p>a few other observations from the day:</p> <ul> <li>myself and some others expected possible PR conditions: atv trails, rolling hills without too much elevation, cool temperatures. by the end of the day, myself and some others had put in our slowest and most diffucult (and well-earned) 50Ks</li> <li>one runner ran an “accidental” ultra: he had planned for 11 miles, never previously ran more than 20 miles, but through latent talent and willpower, the good (bad) influence of friends, and a willingness to just walk into the unknown, ended the day with a 50K finish</li> <li>really all of the runners were amazing</li> <li>that aid station table. a feast. potluck style ultras are great</li> <li>i miraculously never fell. but it seems that every race i don’t fall, i do lose my hat. usually, it’s a prickly vine or something that pulls my hat off, this time it was a thorny tree branch, but at least i scored a point for knocking the branch down too.</li> <li>race execution went well; aid station visits were relatively quick (though i still have much to learn), nutrition and hydration felt just right, which is something i am trying to work on</li> <li>legs were sore and tired the last ten miles (especially when stuck halfway over a log, trying to maneuver legs that don’t want to move), it took a lot of willpower to run with any speed the last five miles, but i had just enough mental/physical energy left to finish with some modicum of strength</li> <li>mud is so much harder than hills. eleven minute miles felt like a sprint sometimes. applied the lessons learned from last year’s muddy Finger Lakes 50s: ran the runnable sections hard, and did a water/calorie/recovery check and aid station pre-planning every time i was forced to walk</li> <li>having grown up in the southern tier of NY, this land felt like home. colin and I talked about this during the race: it’s hard to explain, every forest and hill has a certain identity and locality, so you can feel in your bones when you’re in the land you came from</li> <li>getting to choose the direction and sequence of the last two laps was great. a confidence boost to get the muddy loop done early and in the preferred direction, and to look forward to a “fast” finish on the most runnable end of the “dry” loop</li> <li>colin is a most excellent RD and he needs to continue doing this stuff. my award was maple syrup made by his family from trees on their property. finisher medals were hand made by colin right in front of us. very cool. thanks for everything Colin.</li> </ul> Mon, 04 Apr 2016 00:00:00 +0000 https://www.runningcode.net/2016/04/04/palmers-pond/ https://www.runningcode.net/2016/04/04/palmers-pond/ Introducing TouchGradientPicker <p>One of the features of my new iOS app <a href="https://www.esker-apps.com/dayly/">Dayly Calendar</a> is the ability to pick a color theme for each of your calendars. The default is a simple black and white theme, but the user can also select a colorful gradient for the calendar background, and some complementary tint colors for the calendar grid and buttons.</p> <p>I wanted to give users the freedom to follow their creativity and make color theme selection a very personal choice, while ensuring that it wasn’t possible to choose a theme that would make the app unusable. A short predefined list of of gradients would be easy and safe to implement, but also too limiting and presumptuous. I could have taken a page from image editor software and presented color pickers or slider controls for each endpoint of the gradient, but that felt too technical, complicated, and impersonal. I wanted this feature to be fun, to add a little <em>user delight</em> to the app.</p> <p>The solution was to create a color gradient control that gave users a visceral feeling of directly manipulating colors by embracing the analog nature of touch gestures. Touch is imprecise, but intuitive and multidimentional. Associate various touch gestures with individual color components in a gradient, and you can create an interactive space in which the user can explore gradients.</p> <p>A gradient is defined over several dimensions (three dimensions of color components, and at least two colors) of floating point values. I found that just two dimensions of direct manipulation—horizontal and vertical touch gestures (yes multitouch could increase the number of dimensions but I decided it was unnecessary complexity)—gave the user a surprisingly open space for exploring gradients. By carefully choosing what color components the gestures map to (in this case, hues), and keeping other color components (saturation/brightness/opacity) constant, I could allow the user to explore an extensive palette of gradients while ensuring that every possible gradient would work well, look good, and provide sufficient contrast to the text and buttons in the app.</p> <p><a href="https://github.com/mmertsock/TouchGradientPicker">TouchGradientPicker</a> is the result of this work. It’s an open-source UIKit library that provides two controls: <code class="language-plaintext highlighter-rouge">GradientView</code>, which simply renders a gradient, and <code class="language-plaintext highlighter-rouge">TouchGradientPicker</code> itself, which translates touch gestures into transformations on a color gradient. It’s highly customizable, allowing you to determine exactly how gestures map to transformations on the gradient. Head over to <a href="https://github.com/mmertsock/TouchGradientPicker">GitHub</a> to check it out and see some demo code, and then clone it (or set up a <a href="https://github.com/Carthage/Carthage">Carthage</a> dependency) and add it to your app! <a href="https://iosdev.space/@mmertsock">Let me know</a> if you include it in your app, and submit those pull requests.</p> Fri, 18 Sep 2015 00:00:00 +0000 https://www.runningcode.net/2015/09/18/touchgradientpicker/ https://www.runningcode.net/2015/09/18/touchgradientpicker/ Twisted Branch 100 km Trail Race <p class="editorial-note">This is a very long story. But I don't really know how to condense it. You probably want to skim the first couple of sections and then skip to the <a href="#final-thoughts">end</a>.</p> <blockquote> <p><em>Update:</em> several friends with whom I shared the day have offered their perspectives on the race: <a href="https://web.archive.org/web/20180204120947/http://www.christopherobrien.net/blog/twisted-branch-100k-2015">Chris</a>, <a href="https://dlopata.wordpress.com/2015/09/10/the-usual-suspects-old-skool/">Dan</a>, <a href="https://liveagoodlife.postach.io/post/twisted-branch-2015-a-pacer-volunteer-ultra-loiterer-s-perspective">Jason</a>, <a href="https://hahaitsjeff.wordpress.com/2015/09/08/no-oh-no-i-cant-be-done-yet/">Jeff</a>, <a href="https://onbelaydesigns.wordpress.com/2015/09/06/twisted-branch-100k-2015/">Laura</a>, <a href="https://pequalsmv.wordpress.com/2015/09/10/i-literally-shaved-my-leg-for-this-race-a-twisted-branch-100k-race-report/">Matt</a>, <a href="https://gooseadventureracing.wordpress.com/2015/09/09/2/">Rob</a>. Every one of them showed incredible strength and spirit as they fought challenges that I can barely imagine, or executed an amazing race performance (actually they all did both).</p> </blockquote> <p>On August 29, 2015, myself and a bunch of friends had the privilege of running (or crewing/pacing/volunteering/photographing) the inaugural <a href="https://www.twistedbranchtrail.com">Twisted Branch 100K trail race</a>, a point-to-point ultramarathon on the rugged trails of the Finger Lakes region in upstate New York.</p> <p>My story is similar to many others: I signed up January 1 and trained all year for this race. My first 100K, and fourth ultra. My first ultra was less than a year ago. This was <em>the goal race</em>. A lot was on the line on race day, but everything leading up to it was just as important. To quote a pre-race Facebook post:</p> <blockquote> <p>Before I’ve even reached the starting line, Twisted Branch has had a profound impact on my life: I’ve met new people and developed friendships, tested my limits, found new capabilities of endurance and speed and strength, discovered an amazing trail system, and on and on. While I didn’t have a coach or formal training groups or plans, there are so many of you who have pushed, taught, encouraged, supported me over the months. Tomorrow is a big day, but regardless of how it plays out, every day leading up to this has been pretty great thanks to you. I’ll see you at the starting line/aid station/finish line/behind the camera/in the crew car.</p> </blockquote> <p>Many of the <a href="https://www.strava.com/activities/301354951">races</a> <a href="https://www.strava.com/activities/315125540">leading up</a> <a href="https://www.strava.com/activities/339525772">to this</a> were training for Twisted Branch. I trained far more, and harder, this year than any previous year. Training was almost exclusively on trails in July and August, and I spent hours honing power hiking and technical running skills. Peak training was a 70-mile week with multiple 20+ mile race course preview runs. A lot of us went into the race with injuries or having just recovered from them (I had just finished recovering from a bruised rib, the result of a hard fall down a hill). Through simple attrition, the race was taking its toll even before we reached the starting line. But that fall forced me to have a proper taper period, and on race day I felt strong.</p> <p>My goals for the race were pretty simple: enjoy the trails, spend the day with friends, push a little harder than previous races and find out what I was made of, and get to the finish line.</p> <h2 id="start-to-aid-station-1-cutler-6-miles-a-quiet-start-to-a-big-day">Start to aid station 1 (Cutler), ~6 miles: a quiet start to a big day</h2> <p>5 AM, at the starting line, the sense of anticipation was palpable. But the sense of community was there too. I’ve made many new friends as a consequence of training for this race, and we were all there together. And new bonds were formed, if only for the day, as ultrarunners from out of state introduced themselves. Competition against each other was not on our minds; we knew we were all in this together, and that we were part of something special.</p> <p>The race began with little ceremony, under the moon on a hilltop, the starting line an archway of lights. Soon we were carefully threading through singletrack (not too technical but a good warm-up for what was to come, especially with the darkness factor), and then down the first big descent.</p> <p>The forest was dark except for the procession of headlamps and reflective gear. There’s a beaver pond at the bottom of the hill—a peaceful wilderness sight by day—but it was still too dark to catch any glimpse of it, just an unsettling void off to the side of the trail. The only sounds were the crunch of feet on loose stone and the quiet, uncertain chatter of racers nervous about the upcoming challenge. The woods were otherwise completely silent during these initial miles; we wouldn’t even hear from the birds for some time.</p> <p>So far, the first half hour felt like a rather solo effort, focused on the trail as we all sorted ourselves out in the dark. But when we found ourselves in a meadow in the first valley, in the first light of dawn, I was suddenly surrounded by a group of friends: Chris, Jeff, Josh, Laura, and Matt. It was time to tackle the first climb, and we stuck together, building confidence as we power hiked 400 ft of hill shoulder to shoulder.</p> <p>Ron (one of the <a href="http://www.theascendcollective.com">Ascend Collective</a> photographers documenting the day) was camped out at the overlook, waiting for what promised to be a spectacular sunrise backdrop. As expected, we passed the overlook before the sun was up, while discussing whether we were going out too fast. The consensus was no, we were banking miles in cool weather on the “easy” part of the course. Back down into the valley, and I started to feel out my downhill strategy for the day. The extra momentum as we descended switchbacks on the dark north side of the hill led to a couple of my few missed turns; thankfully there were several of us navigating together to catch the mistakes immediately.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/sunrise.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/sunrise.jpg" /></a> <figcaption>Sunrise over the Bristol hills. The trail would pass through these hills, all the way to the horizon. Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <h2 id="as-1-to-2-naples-creek-65-mi125-total-making-some-choices">AS 1 to 2 (Naples Creek), ~6.5 mi/12.5 total: making some choices</h2> <p>Soon we were at the first aid station. I was pleased to see that the “no crew access” aid stations were better stocked than I anticipated, and operated by several volunteers. I hadn’t quite finished a single 18 oz bottle: a sign that I would get behind on hydration, but the cool and humid weather convinced me that there was no problem. A quick Tailwind refill (another logistical perk of this race was Tailwind at every aid station) and a couple bites of food and I was off to rejoin the group.</p> <p>We were on roads for the next 1.7 miles, one of the few road sections in the race. Our group spread out a little, but we maintained pairs or triples as we traded places a bit, some of us stretching out legs on the gentle road slopes, taking in the views of the now-risen sun over country fields, and sharing lame runner jokes. The day was going well, we were in pretty good spirits, and clearly still “warming up”. But small decisions made early on can dramatically alter one’s experience in an ultra, and I was about to make a seemingly inconsequential choice that would set the tone for the rest of the day.</p> <p>As we came down a little drop toward the end of the road segment, I decided to pull ahead a little, leaning with gravity to pick up some speed. We had a bit of flat easy trail ahead, I was feeling good and the weather was still cool, and I wanted to pick up some time and get some mental momentum going into the next climb. As I left the rest of our running group behind, I figured I would see them throughout the day and probably finish in the middle of that pack. I would never have guessed that this decision would shift my race into a largely solo effort, and that I would be facing the ultrarunning demons on my own terms.</p> <p>The next four miles were a blur of climbing and rolling technical trail, as I found myself working harder in a pack of unfamiliar racers. I hiked West Hill hard then traded places with some runners as we exercised our respective strengths and weaknesses on the varying terrain. Coming into this race, I wanted to push a little and test some limits, having finished every previous ultra with some gas in the tank and wondering how much harder I could have worked. This section is where I started to follow that intention, and I would spend much of the race chasing these limits.</p> <p>The sun started to show its strength through the sparse canopy on the east side of the hill as we dropped 1000 ft into Naples. This was one of the largest single descents, and actually one of the easiest with somewhat soft trails on relatively decent footing and fresh legs, though the switchbacks and oddly terraced terrain contributed to what would be an overall punishing run for most of us. A steady stream of us crossed town and rolled into the Naples Creek aid station.</p> <h2 id="as-2-to-3-brink-hill-57-mi182-total-alone-in-the-woods">AS 2 to 3 (Brink Hill), ~5.7 mi/18.2 total: alone in the woods</h2> <p>I tried to be quick at the aid station, filling bottles, stocking up on food, and cleaning off some orange paint that was mysteriously all over my hand. This was the first aid station with crew access, and Valerie and others were there to give us a boost (Valerie’s efforts were extraordinary all day). I caught what would be my last glimpse of Laura and the others as I took off back into the woods. Up ahead was High Tor, the biggest single climb of the race: 1112 ft over 1.6 miles.</p> <p>The climb went pretty smoothly; I hadn’t seen the lower portion in a couple of years, and it was easier than I remembered. The upper portion can be a big mental challenge, as it goes on and on with a lot of false hope of summiting; having climbed this section (and some of the earlier hills too) last winter made this feel a little easier. I got to the top feeling pretty strong and had a bit of easy forest road to shake it off. But this section was far from over, waiting ahead was 3.5 miles of very technical trail with a few hundred feet of climbing that goes unnoticed while scrambling through rough footing. But the surface isn’t the only thing distracting from the climb: High Tor is a very special area with beautiful scenery, and this part of the trail traverses lush carpeted forest overlooking Conklin Gully (the gully leads to a 120 ft waterfall, just out of sight of this race course).</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/conklin.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/conklin.jpg" /></a> <figcaption>Conklin Gully. Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <p>All this added up to a long time alone in the forest, noticing some real fatigue for the first time as the big hill and now the steady technical climb took their toll. This slow-going remote trail usually offers a lot of opportunity for thinking, but today I just focused on reaching the upcoming aid station and keeping a rhythm as best as possible. Eventually, I ducked out of the woods onto a back road and the Brink Hill aid station (no crew access).</p> <h2 id="as-3-to-4-italy-valley-44-mi226-total-crashing-down-the-hill">AS 3 to 4 (Italy Valley), ~4.4 mi/22.6 total: crashing down the hill</h2> <p>After Brink Hill is about a mile of paved and dirt roads, and a few hundred feet of additional climb. A handful of us were in sight of each other again, and most of us took the climb pretty slowly. The easy road running was over; this was a slog, as would be all the remaining roads. This road caps off 640 ft of nearly uninterrupted climbing when combined with the trail before the aid station—a detail that went unnoticed in preview runs—finishing a 6.3 mile section with over 1750 ft of climbing. The race had officially become hard.</p> <p>We finally got a break from the climbing and were rewarded with some incredible views to the west and southeast. Mike (the other Ascend photographer) was at one of these overlooks capturing one of the iconic scenes of the race. The break was soon over, though. After entering the woods, the course returned to increasingly technical singletrack, and then increasingly steep downhill work.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/overlook.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/overlook.jpg" /></a> <figcaption>The Italy Valley overlook. Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <p>Thankfully, some of the massive amounts of fallen trees and torn trails were cleaned up, the many turns were well marked, and having previewed this recently I was sufficiently familiar with the trail to just focus on moving forward. It was a tough descent though, 850 ft down in just over a mile, on loose shale and washouts (we would become intimately acquainted with rocks and ruts in this race), switchbacks, and unrelenting steepness (much of it 30% average grade); it required intense focus to go down this smoothly. Finally the woods ejected us into a steamy valley meadow just outside of Italy Valley aid station.</p> <h2 id="as-4-to-5-italy-turnpike-67-mi293-total-breaking-down">AS 4 to 5 (Italy Turnpike), ~6.7 mi/29.3 total: breaking down</h2> <p>We were glad to see our crews (in addition to Valerie, it was nice to see Jason and Mike). While filling bottles, I took a couple of extra minutes here to regroup and get focused. The first 1/3 of the race was finished and felt pretty good; the next 18 miles would get me to Bud Valley, a major aid station and pacer pickup point for some of the runners. I knew this section could be a bit of a grind on its own, let alone in the middle of an ultra. We were sent on our way to the next climb (a theme of this race: most aid stations are in deep valleys).</p> <p>Italy Hill presents a long, steady ascent, and you can see uncommonly far as the trail is straight and follows a consistent moderate grade (675 ft over 1.7 mi). The leaders ran this hill, but the rest of us were resigned to hiking. The scope of the race was starting to sink in as I walked up the hill alone. With easy footing and a light effort, the mind had plenty of time to wander and dwell on things. Valerie was keeping track of the other runners I knew, and the latest news indicated I would be alone for a long time. Most of my friends were somehow behind me. Daven, the eventual winner, already had a one hour lead; of course I had no intention of being anywhere near him, but an hour felt like an enormous gap this far into the race. I had hoped to finish somewhere in the neighborhood of Dan, but he had a decent and steadily growing lead. Right now, he seemed as unreachable as the guy I spotted way up the hill ahead of me.</p> <p>After the climb, it was back to work in Italy Hill State Forest, through rolling mud (about as dry as you could ask for though), grass, and rocks. The air was close and felt hotter than expected on top of the hill. After a lot of work, much of it alone, I started the final climb out of the woods, transitioning from a (very) washed out track to paved road. Then, everything fell apart.</p> <p>I’m still not certain what exactly broke me down, some combination of factors for sure, but I didn’t see it coming and haven’t yet puzzled out all the details. Italy Hill was barely the longest (so far) segment between aid stations, but it felt much longer. I was suddenly very hot and fatigued, off balance mentally, and discouraged after being passed by some rather fresh looking runners (including Wookie and Jeremy: racers from Baltimore whom I would get to know during the race). Jeff’s parents were on the trail taking photos, and their cheers provided a much needed boost but the effect was only temporary. I tried to eat a Picky bar, but I was suddenly rather dehydrated and couldn’t chew it. And then I stopped, on pavement and exposed to the sun, to reboot a frozen GPS device. The plan was to take advantage of two miles of downhill road leading up to Italy Turnpike aid station. I put in the work, but it hurt and felt deathly slow in the heat.</p> <h2 id="as-5-to-6-patch-road-63-mi356-total-a-long-recovery">AS 5 to 6 (Patch Road), ~6.3 mi/35.6 total: a long recovery</h2> <p>Italy Turnpike was all orange, an unofficial <a href="https://www.trailsroc.org">#TrailsRoc</a> aid station full of experienced runners. This was fortunate placement: the race had got serious and the heat was taking its toll on all of us, and this crew knew how to take care of us. I spent a long time at this aid station trying to cool off, rehydrate, and get positive. I was introduced to the wonders of pickle juice. Crew and volunteers corralled me into the shade as I staggered around the tables. After the race Valerie informed me that I was a complete mess at this aid station.</p> <p>After more than 15 minutes at the aid station, I ventured back out to the trail and got back to work. Hydration and staying cool was now the primary focus. I saw Ron taking photos again. Each time he saw me and asked how I was doing, the answer grew less positive. It was pretty bleak this time.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/turnpike.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/turnpike.jpg" /></a> <figcaption>Feeling uneasy after Italy Turnpike. Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <p>I spent the next hour gradually recovering, finding my strength again and passing a few people. But there was only so much recovery possible 30 miles into an ultra. I was clearly not 100% lucid, as I was briefly convinced we had already passed the next aid station after less than two miles, and then thought it was less than a mile away, but there were still several miles ago.</p> <p>We wound through fields (including a <a href="https://youtu.be/pU3hZPN6qYc">direct line</a> across a full-grown corn field), forests, gullies, stream crossings, another hot road, dry stream beds rattling with large loose stone, thickets and washed out jeep roads, and again I was thankful to have previewed most of the course. Otherwise the aid station confusion and the rugged trail would have made for a long mental challenge. As it was, I could barely remember the sequence of landmarks through here, but eventually Patch Road aid station appeared.</p> <h2 id="as-6-to-7-bud-valley-42-mi398-total-new-problems">AS 6 to 7 (Bud Valley), ~4.2 mi/39.8 total: new problems</h2> <p>Patch Road had a campground atmosphere, with rustic seating in a forest clearing and guitars twanging on the porch. Wanting to keep the positive feeling going, I was careful to avoid the urge to linger, and stayed only long enough to cool off a bit and refuel. Back to the trail, and the rolling hills gave way to some short but steep climbs and descents. The next aid station was not far away, but the trail continued to wear on us.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/patch.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/patch.jpg" /></a> <figcaption>We ran on a whole lot of rocky, washed out stream beds and abandoned roads. Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <p>Coming down a steep hill about halfway through this stretch, I suddenly felt some pain around the knee cap, like a classic runner’s knee. This came out of nowhere: I had felt fine all day and hadn’t had any knee problems in years. Descents were painful, flats and climbs were fine. Keeping moving, I attempted various tactics for the downhills. They all hurt the same though, so I resigned to just running down the hills and getting it over with sooner.</p> <p>Bud Valley aid station had a big crowd, with a well stocked aid station, crews, and pacers waiting to join their runners. Since I had problems chewing a food bar again, I took some time to thoroughly cool off and drink a lot of water while Valerie fetched some naproxen. Somehow I spent over 15 minutes at this aid station before leaving with a mix of concern for the knee and optimism from the crowd.</p> <h2 id="as-7-to-8-glenbrook-road-64-mi462-total-finding-strength">AS 7 to 8 (Glenbrook Road), ~6.4 mi/46.2 total: finding strength</h2> <p>The exit from Bud Valley is a washed out climb that gradually turns to smooth, increasingly sunny dirt roads. Now fully aware of the heat and hydration challenges, I was careful to stay in the shade when possible, and paid more attention to effort levels and water intake. I knew that the upcoming six miles would present a lot of very runnable sections—great opportunities to make up time—and only one steep descent, so the knee should get a break. I started pushing a little harder.</p> <p>Several unexpected developments improved spirits even more. At the end of the dirt road, a landowner had put out a water jug and a hose and was cheering us on. The steep descent, a maze of normally hard-to-follow switchbacks, was flagged well, and the soft surface minimized knee pain. In the valley, the trail follows the edge of some fertile bottom-lands and was recently overgrown with chest-high stinging nettles, but some volunteers had cut back all the nettles. I started catching up and passing some now-familiar runners, exchanging comments such as “there you are” and briefly discussing how we were doing. Otherwise strangers, we were developing some real bonds and concern for each other on the trail.</p> <p>Feeling strong and confident, I pulled ahead through a gently climbing meadow and into open woods, putting together a good power hike up the steep hill into Urbana State Forest (430 ft over 0.7 mi), fending off some new runners who had appeared behind me. I had been looking forward to the next 2.3 miles—relatively level and perfectly runnable trails through scenic open woods—and picked up the pace as much as I could, working progressively harder, getting close to a 10 minute pace on the way to Glenbrook aid station.</p> <h2 id="as-8-to-9-end-of-bristol-hills-trail-8-mi542-total-lost-and-found">AS 8 to 9 (End of Bristol Hills trail): ~8 mi/54.2 total: lost and found</h2> <p>I blew into Glenbrook aid station, glad to see my crew and some familiar faces running the aid station, and they immediately got to business filling bottles and fetching ice. The day was cooling off and I finally felt in control of hydration. I was feeling very strong and happy with the recent miles, and shocked to hear that I was getting close to catching Dan. This was a whole new race.</p> <p>These good vibes came at a crucial moment: the next aid station was eight miles away and this section the only part of the course that I hadn’t previewed. Regardless of preview runs, I figured miles 46 to 54 had a high chance of stretching on forever, similar to the 30s of a 50-mile race. It was time to get serious with the mental games to stay in control.</p> <p>I took off alone down the hill (the only really downhill aid station exit), scrambled up the last bit of trail I knew in this area (a nice boost to know the steep climb wouldn’t last long), and followed a short road stretch before entering Pigtail Hollow. The short road section turned into a long one, and I realized I probably missed a turn. Backtracked a bit and found the turn, and ducked back into the woods. These were deep woods, and I was glad to have a built a reserve of strength and positive energy, as this area would definitely have been much harder otherwise.</p> <p>The horse flies found us somewhere in Urbana forest and were relentlessly circling ever since. I resorted to carrying a stick and swatting at them as they passed, but dropped the stick near the aid stations so I wouldn’t look ridiculous.</p> <p>Just as things were starting to feel lonely and difficult, I caught up with Wookie (whom I later learned used Twisted Branch as the kickoff to a 45-day trail running <a href="https://wookierunsamerica.com">adventure</a>) and Jeremy, and hung with them for a while. We had been passing each other all day, and now spent the next couple of miles together, supporting each other through the long remainder of Pigtail Hollow and the descent to the road crossing, passing the time with conversation. As the grade steepened, the knee pain increased, so I passed them and sped down the hill to get it over with quicker.</p> <p>After the road crossing was the 50-mile mark, a water cache and then a wall of a climb, 350 ft straight up, and I pushed pretty hard. I was ready to finish this race. The ultra math kicked in, counting down the remaining miles and hours with aggressive rounding down. There were still a few unfamiliar miles to the next aid station and the leg pain was spreading, so I could have lost momentum running alone here, but Mike was out taking photos and then I caught sight of another runner and made an effort to really race and try to pass her. This dumped me down into the next aid station a few places up.</p> <h2 id="as-9-to-10-urbana-5-mi592-total-this-is-an-ultra">AS 9 to 10 (Urbana): ~5 mi/59.2 total: this is an ultra</h2> <p>Aid station 9 was on an abandoned road in the middle of the woods with no crew access, but it was a major milestone: less than ten miles to go, and it marked our completion of the entire Bristol Hills branch of the Finger Lakes Trail system. Even if runners dropped, anyone who made it this far could claim an impressive one-day end-to-end traverse. The volunteers called it “Pub 54” and put out some festive lights and signs, a great idea for those who got here later in the day.</p> <p>Dan was leaving just as I arrived at the aid station, an unexpected sight, so I hurried to fuel up and get out of there. There was a lot of work to do, but it felt like the beginning of the end. It was time find out what strength I had left. Soon I was climbing with Dan up some tough switchbacks as we discussed the day. After this was a rather steep and rough drop to the next road crossing. I pulled ahead of Dan, gritted my teeth, and finished the downhill as quickly and smoothly as possible over the protests of the leg.</p> <p>After a road crossing and climb over a gate was Mitchellsville Creek gorge, an exceptionally beautiful area. The trail drops 765 ft over 1.7 miles of soft trails winding through open hemlock forest overlooking the gorge in the evening light. Pushing hard, I managed a sub-10 pace through this; it would have been nicer to find even more speed but it was what I could muster given the leg issue.</p> <p>These five miles up to the last aid station were a true ultrarunning experience, like I had never had before. I took a fall, somehow the only fall of the day, and landed on soft dirt and pine needles. Getting up required massive effort, not because the fall hurt—it was a very soft landing—but because I wanted to just lay down and sleep. I was so tired, more tired then I’ve ever felt. But after getting up I was amazed that I could still move, and move somewhat quickly. They say you are stronger than you think. I don’t know where it came from, but I found strength here to climb steep hills and run down hills, and do it better than in some short races in the past. The desire to finish was everything, I was focused in every possible way on that goal, fighting the increasing parts of my physiology that wanted to stop the abuse.</p> <h2 id="as-10-to-the-finish-5-miles64ish-total-giving-it-everything">AS 10 to the finish, ~5 miles/64ish total: giving it everything</h2> <p>Through a vineyard (a reminder after hours in the woods that we’re in the Finger Lakes) and we were at the last aid station. I downed some cola and grabbed a couple of caffeinated gels for a final energy boost, and quickly addressed a hot spot. I was only there a few minutes, but suddenly the legs were very stiff. The hard work of the last several miles was catching up, and I think my body was trying to make me stop. It was time to face the final climb though, and I shuffled off to the road crossing, shaking off the stiffness and pain.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/urbana.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/urbana.jpg" /></a> <figcaption>Coming into Urbana aid station.</figcaption> </figure> <p>Mount Washington loomed ahead. I had previewed this hill and knew that while it was long and steep, it at least was not technical and I had already worked out the navigational mistakes. Into power hiking mode, and up 813 ft of switchbacks over one mile. Stopping was not an option: this last hill would probably break anyone who stopped. Knowing the route allowed me to just go into that mental cave and focus on moving the muscles. I visualized climbing it the previous week on fresh legs and followed that vision as best as possible. Katie had left the aid station a bit ahead of me, and she put in a very strong climb, but with much effort I caught back up somewhere on top of the hill.</p> <p>Running with someone now, I let my guard down and found that there was nothing left. Every last bit of strength had been used on that climb, but the race was not over. We had 2.5 miles of rough rocky trail weaving up and down the side of the ridge before the final descent. Even though I had seen this trail just a week prior, this section went on forever; I can only imagine what this must have been like for the runners who were unfamiliar with it or had to run it in the dark. I was lucky enough to have some light left, it would be a race to beat the sun to the finish line.</p> <p>We dragged ourselves along the ridge, trying to encourage and push each other. It was all I could do to keep up with Katie, form was falling apart and it required immense effort to navigate the rocks and roots. She encouraged me to keep up, and I tried to keep us positive by narrating what I remembered of the trail. We hit the road and stumbled along it, a quarter mile downhill, before entering the Triad Trail, the final test.</p> <p>I told Katie to lead, down into and out of a gully. I had imagined running this last little 100 ft climb (because why not, it was the last climb), but it just did not happen, and she pulled ahead. Finally, there was less than a mile left, switchbacks down the remaining 500 ft of the hill. I was so glad to hear and then see the traffic of the last road crossing, and the crowd at the finish line. I slid down the bank to the road, and as the sun set I was overwhelmed with joy to cross the finish line. After nearly fifteen hours, it was over.</p> <figure> <a href="https://www.runningcode.net/assets/tb100k/finish.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/finish.jpg" /></a> <figcaption>Finished! Photo by <a href="http://www.theascendcollective.com/">The Ascend Collective</a>.</figcaption> </figure> <h2 id="final-thoughts">Final thoughts</h2> <p>There are a lot of words up above, but they still fail to really capture the experience. I will be trying to understand that day for a long time. It was the hardest thing I’ve ever done, and I worked harder than ever before. It was unforgettable.</p> <p>Ultrarunning exposes you to challenges that just don’t happen in other types of races. Utter mental and physical exhaustion. The odd feeling of running in the pre-dawn hours. Being completely alone for long periods of time, but at other times, sharing the experience in a very direct way with others. Connecting in a very physical way with the natural world. Searching for the thin line between taking it too easy and the disaster of going too hard. Somehow, through pure force of will, finding the strength to just keep moving and even running when it seems that nothing should be left. Pushing through hours of pain and discomfort. Learning that you can just keep going, and going and going, and not allow your body to shut down until you are finished. How much effort to put into an ultra? More than you think you can is the proper answer in my opinion.</p> <p>This was a difficult, sometimes brutal course: about 64 miles, nearly all trail, most of it technical with about every surface you could imagine. Up and down all day with 10,000 ft of climb and 11,000 descent, no mountains but a good handful of long hills and steep banks. But we weren’t fighting the trail. We weren’t even competing with each other (most of us, for the most part). We battled our own perception of limits. People often say that about things like running, and I’ve had some idea of what that means in the past, but in this race I really experienced what it was like to transcend your own limits and fears and ego. That is what ultrarunning is really about, I think.</p> <p>There were some mistakes for sure, and some things I want to improve for next time: primarily I want to figure out the 30 minutes spent at Italy Turnpike and Bud Valley aid stations: was it really necessary? Could it have been avoided? One clear lesson is the importance of cooling one’s core. Otherwise, I’m happier with this than with any previous ultra.</p> <p><a href="https://www.runninginsideoutpodcast.com/guests/scott-magee">Scott Magee</a>, the race director, devoted years of his life to making this race a reality. He put together a nearly flawless event, and this was the first year of the race and his first ever (!) time as RD. He won over the Finger Lakes Trail Conference and countless landowners, got the local communities excited about the event, and navigated a lot of red tape. He was even out on the trail a week before the race, literally building the last mile of the course so we didn’t have to finish on roads. This is a world class race and I hope it gets the attention it deserves in the coming years.</p> <p>The volunteers (almost as many volunteers as racers) and aid stations were all excellent. It was great to see a lot of friends and “running family” out there. And of course, a thousand thanks to Valerie, who put up with months of training and ultra talk, and had a very long day crewing us. Her help was critical throughout the day. When I wasn’t feeling great and couldn’t think straight, she was there and knew exactly what to do.</p> <h2 id="some-technical-notes">Some technical notes</h2> <ul> <li><a href="https://ultrasignup.com/results_event.aspx?did=30894">Race results</a>: 14:44:32, 14th overall, 5th in age group. My best performance of any ultra. The funny thing is that early in the year, 12ish hours sounded like a good goal time. As the months went by and we learned about the trail and got more training experience, that goal time got later and later. Even the winner finished in 11:45—in hindsight, 12 hours was completely unrealistic.</li> <li>Race data: available on <a href="https://www.strava.com/activities/381368405">Strava</a>.</li> <li>Shoes: Altra Lone Peak 2.0. On my third pair of these. They are the most comfortable shoe I’ve ever worn and they have handled every challenge. The durability of the upper can be a problem though, and my primary pair had some small but growing holes. I decided to stick with them and brought a newer pair for backup. But they performed perfectly all day and the holes behaved.</li> <li>Other gear: Ultimate Direction SJ Ultra Vest and Body Bottles, North Face Long Haul shorts, PEP hat. I’ve been going more minimal with gear lately, but opted for the vest to keep the hands free on the climbs. The soft bottles are worth the investment: no sloshing and all discomfort eliminated. <a href="http://www.pettetendurance.com">PEP</a> offers American-made wool clothing, and their hat is cheap and awesome.</li> <li>Calories: a roughly even mix of tailwind, gels, and real food. I used Picky bars and Clif purées early in the race, but with the mild dehydration and the hint of poor digestion, I switched to the Munk Packs offered at the aid stations as they were much easer to suck down and digest.</li> <li>GPS: I think <a href="http://www.ismoothrun.com">iSmoothRun</a> on iPhone (thrown in back of the vest in a Loksak), paired with an Apple Watch, is as good as any GPS watch these days. I get 10 hours or more of battery life, and a phone case with builtin battery more than doubles that. I also loaded the official <a href="https://fingerlakestrail.org">Finger Lakes Trail maps</a> on the phone, helpful for the preview runs and figuring out that wrong turn at mile 48.</li> </ul> <figure> <a href="https://www.runningcode.net/assets/tb100k/gear.jpg"><img src="https://www.runningcode.net/assets/tb100k/thumbs/gear.jpg" /></a> </figure> Mon, 07 Sep 2015 00:00:00 +0000 https://www.runningcode.net/2015/09/07/twisted-branch/ https://www.runningcode.net/2015/09/07/twisted-branch/ On Writing APIs: Collaborating Effectively With Your Mobile Team <p>API-driven mobile app development requires more than good engineers and the latest technology. Whether your organization is a startup, or an established company expanding into the mobile space, you may be creating the app and API in parallel. The mobile and API teams will need to collaborate effectively in order to launch a successful product. In fact, I would say that communication and teamwork can be more important than the choice of server-side and client-side technologies.</p> <p>Mobile and API developers can write great code in their respective silos, but if the interaction between the two teams is not top-notch, the interaction of the app and API will reflect that. Teams will end up with divergent understanding of the work. Timelines will get out of sync. Developers will be fighting to get the client and server to work together as anti-patterns and incompatible interfaces emerge. Every new feature will repeat a cycle rehashing the same bugs, fighting the same structures, duplicating slight variations of boilerplate “solutions” to the architecture problems. Friction will rule both the programming work and team dynamics.</p> <h2 id="eleven-tips-for-successful-collaboration">Eleven tips for successful collaboration</h2> <p>Regardless of the technology in use or the scale of the project, there are several communication, design, and implementation techniques that API and mobile teams can use to improve their success.</p> <h3 id="a-shared-vision-and-common-language">A shared vision and common language</h3> <p>Have the API team involved directly in the mobile app work. Developers from both sides should collaborate on the initial design decisions. A common business language prevents developers from having to constantly translate concepts to and from the mobile and API domains, thus reducing frustration and misunderstandings. By working on some shared user stories and common milestones, there are fewer conflicts of resource allocation and prioritization between API and mobile teams.</p> <h3 id="teams-should-work-closely-together-at-all-levels-on-a-daily-basis">Teams should work closely together, at all levels, on a daily basis</h3> <p>Teams should share stakeholders and project/product managers. There should be daily communication between developers; perhaps both teams or at least representatives from each have a shared Scrum session if logistics allow.</p> <h3 id="define-the-api-interface-based-on-user-stories-and-business-needs">Define the API interface based on user stories and business needs</h3> <p>An API interface should speak the language of the API consumer, <em>not</em> the underlying technology. A common naïve API implementation may look like a thin <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> layer over a database. This may be a design smell if the domain model and use cases do not mirror the database that closely. With modern frameworks and development tools, there should be minimal pain in translating API requests to the underlying data model.</p> <h3 id="solve-business-needs-rather-than-technical-requirements-to-achieve-roi-with-reusable-apis">Solve business needs rather than technical requirements to achieve ROI with reusable APIs</h3> <p>In addition to allowing the interface to be dictated by the server database, another API design smell is directly modeling the internal structure of the mobile app. This will lead down the slippery slope of mobile app changes requiring versioning the API, and all the complexity that implies.</p> <p>Instead, API endpoints should describe business objects and operations, and parameters and data types should further reflect the business language for specific use cases. This allows the API to potentially be usable by a variety of clients (e.g. your website or third party integrations), and can force better and more maintainable implementations on both the client– and server-side. Note that coordinating focus on higher level business needs requires the above practices of shared vision and daily team interaction.</p> <h3 id="implement-both-the-client-and-server-incrementally">Implement both the client and server incrementally</h3> <p>Think Agile—both teams should start by implementing a single use case end-to-end. Start with one fully functional API endpoint and get the mobile app using that API. The teams continue to work together, incrementally building out more API and client functionality, directed by user stories. This keeps the teams in sync. As a rhythm of design, implementation, and testing emerges, teams can optimize their resource allocation (think Kanban).</p> <h3 id="establish-patterns-and-make-the-api-self-documenting">Establish patterns and make the API self-documenting</h3> <p>Hand-written documentation has its place, but make it a goal to achieve a largely self-documenting API. A shared language focused on business concepts instead of technical terms, as described above, is essential for this. Use self-evident naming conventions so that parameters and paths are increasingly obvious to users, and find tools to generate user-facing documentation from code comments/attributes and validation rules.</p> <p>With our API, it often felt that we spent half our time working on the interface design in the early stages. This has paid off in reduced cognitive load, as pattern recognition is a human strength. Patterns invoke all the experience gained consuming similar API endpoints as new APIs are added. The mobile team will find the API increasingly easy to use, due to reuse not of code, but of <em>concepts</em>.</p> <h3 id="use-stub-endpoints-to-improve-velocity">Use stub endpoints to improve velocity</h3> <p>When building new endpoints, focus on quickly getting a stub of the new interface in a test environment before writing a single line of code for the actual implementation. This allows the client-side developers to get started on their implementation in parallel, and allows additional time to make changes to the interface if necessary.</p> <p>The API stub should include the entire <em>interface contract</em>—request and response schema, validation/authorization rules, and HTTP status codes—but an empty or hard-coded response body. A good API framework should allow for rapid development of these interfaces with a stub service implementation.</p> <h3 id="provide-tools-for-debugging-qa-and-regression-testing">Provide tools for debugging, QA, and regression testing</h3> <p>The UI of a mobile app is not conducive to debugging and testing API interactions. It is up to the teams to support each other. Developers will appreciate server-side logging, instrumentation, and debugging proxies; the mobile team should make the API hostname, etc., configurable in dev builds; QA teams will find it useful to have tools for directly exercising API endpoints, and can help keep developers focused on the use cases.</p> <h3 id="have-qa-test-the-api-directly-or-write-automated-tests">Have QA test the API directly, or write automated tests</h3> <p>API tests should be focused on use cases, from the perspective of the API consumer or, by proxy, the end user. APIs are a prime target for <a href="https://stackoverflow.com/a/19210183/795339">integration testing</a> or an automated regression suite: API tests are easy to automate, the output is predictable, and stability/backward compatibility is essential.</p> <h3 id="dont-be-afraid-to-change-direction">Don’t be afraid to change direction</h3> <p>If an API or client-side design pattern is causing headaches, be willing to throw it out in favor of something better. You may need to keep some of the old implementation around (for example, in outdated but in-use API endpoints), but it will probably pay off to move forward with a better design.</p> <h3 id="consider-a-pilot-or-prototype-phase-to-buy-time-for-design-experimentation">Consider a pilot or prototype phase to buy time for design experimentation</h3> <p>In both API and the mobile app development, much can be gained by having some time to iterate the large scale design. What looks like a good API design pattern or a good MVC architecture may prove unworkable, but you may need to attempt implementing a few features before this becomes clear. To get the most out of this, both teams should have the opportunity to work together on this phase.</p> <h2 id="the-impact-is-substantial">The impact is substantial</h2> <p>These suggestions are based on my involvement in an ongoing, <a href="https://corp.trackabout.com/modules/smartphone.php">successful</a> API + smartphone implementation. As our teams have worked together using the practices described above, the quality and efficiency of our work has improved with each new feature and API endpoint. Even after months of working together, we still see measurable acceleration in our work as patterns and practices continue to solidify. The API, while having evolved a bit, has proven to be stable and reusable with little versioning needed. Endpoints are being used by both the mobile app and third-party clients, which adds a lot of value to the API work. The mobile app is robust and scaling well as we add new features.</p> <p>Though much of this success can be credited to good tools and technology, I think some other elements are just as important: the consistently close collaboration between API and mobile teams, shared stakeholders and vision, a common and consistent language, incremental and parallel workflow, and mutual support.</p> Sun, 29 Mar 2015 00:00:00 +0000 https://www.runningcode.net/2015/03/29/api-mobile-collaboration/ https://www.runningcode.net/2015/03/29/api-mobile-collaboration/ Meta-Running? <p>A problem lately: I start running, and within five minutes or so, I start thinking about a title or description for my <a href="https://www.strava.com/athletes/mmertsock">online training log</a>. I should be paying attention to the run itself, my body’s performance, the state of hydration and nutrition, monitoring aches and pains or enjoying a rush of energy, checking how this all relates to the day’s training goals. But it is too easy to drift off into more abstract reflection: coming up with a clever description to share on social networks, imagining what others will think, looking for pictures to grab with the smartphone. I just want to run, with a clear head.</p> <p>Take today’s run, for example. The run was a bit of a slog for the first few miles. My attention shifted pretty quickly from the actual state of my body to the need to get back into a serious training regimen, and then about the great training weeks my friends have had, and then I started composing a <a href="https://www.strava.com/activities/268874093">Strava entry</a> about this in my head. And then I began to reflect on how my thoughts have strayed like this too often lately. Next I was thinking of writing a blog post about that observation. I remarked to myself on how meta all that was. By that time, I finally noticed that I had loosened up, the run was no longer a slog, and that I could make it a proper workout with some focus. Of course now I’m blogging about the whole experience, which is also meta.</p> <p>This is a bit of a problem, for me and I think a lot of other people (there is a steady stream of running-selfies out there, for one measure). Sometimes we are too connected; we quantify our runs until we neglect to feel the ground with each step, and we objectify our performance until we no longer feel our lungs fill with each breath. Analysis has become real-time and recursive. Some days I just want to run.</p> <p>Short of investing time in learning to meditate or throwing out all training technology (which really is useful), the only two solutions I know of are to run longer and work harder. Working hard or being out a long time, you will eventually get into a zone in which you are present to the reality of the run. Focus returns, the mind calms, and your senses sharpen. Then you are really running. How do you avoid meta-running?</p> <p>Maybe blogging about it will help.</p> Mon, 16 Mar 2015 00:00:00 +0000 https://www.runningcode.net/2015/03/16/meta-running/ https://www.runningcode.net/2015/03/16/meta-running/ HTTP Request Compression in ServiceStack/ASP.NET <p>Compression of HTTP requests (not responses) is uncommon, so when I needed to support this in a <a href="https://servicestack.net">ServiceStack</a> application, I found it difficult to scrape together comprehensive information about how to implement and test compressed request handling. This post collects in one place everything you need to to know.</p> <h2 id="what-does-a-compressed-http-request-look-like">What does a compressed HTTP request look like?</h2> <p>Compressed HTTP requests are the counterpart to compressed responses: the headers are specified in the request, the client does the compression, and the server performs the decompression:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST /path HTTP/1.1 Content-Type: application/json Content-Encoding: gzip &lt;body content: raw gzip-compressed binary data&gt; </code></pre></div></div> <p>The <code class="language-plaintext highlighter-rouge">Content-Encoding</code> header indicates that the body content of a <code class="language-plaintext highlighter-rouge">POST/PUT/PATCH</code> request is compressed, in this case using GZip. Note that the <code class="language-plaintext highlighter-rouge">Content-Type</code> still indicates the media type of the <em>uncompressed</em> data. Larger requests can be streamed using the <code class="language-plaintext highlighter-rouge">Transfer-Encoding: chunked</code> header (otherwise the size of the compressed data is indicated with the <code class="language-plaintext highlighter-rouge">Content-Length</code> header).</p> <p>Compression is handled independently for requests and responses. The <code class="language-plaintext highlighter-rouge">Accept-Encoding</code> header, which is used to indicate a client’s preference for compressed responses, has no relation to request compression. Depending on the request headers and the capabilities of the client and server, a given session can have any combination of compressed or plaintext request and response.</p> <p>Note that while a server can choose not to compress a response when requested by the client, this optionality is not possible with compressed requests. If the server does not support a given request Content-Encoding, it may refuse the request.</p> <h2 id="does-aspnet-or-servicestack-support-compressed-requests">Does ASP.NET or ServiceStack support compressed requests?</h2> <p>I did a fair amount of research and found no evidence that the ServiceStack framework, or ASP.NET in general, can decode compressed HTTP requests out of the box. Thus, I needed to roll my own implementation.</p> <h2 id="does-the-net-framework-provide-a-means-to-decompress-gzip-data">Does the .NET framework provide a means to decompress GZip data?</h2> <p>Yes, the <a href="https://msdn.microsoft.com/en-us/library/system.io.compression.gzipstream(v=vs.110).aspx"><code class="language-plaintext highlighter-rouge">GZipStream</code></a> class in the <code class="language-plaintext highlighter-rouge">System.IO.Compression</code> namespace can both compress and decompress data using GZip encoding. This class takes a <code class="language-plaintext highlighter-rouge">Stream</code> representing the input data along with an enum indicating the mode of operation (Compress or Decompress). When operating in Decompress mode, GZipStream’s output is raw decompressed bytes, and as a subclass of <code class="language-plaintext highlighter-rouge">Stream</code> itself, the output can be read incrementally, passed to other stream readers/adapters, or read in its entirety into a string or byte buffer.</p> <p>This namespace has similar classes for other compression formats. While this post focuses on handling GZip-compressed requests, other formats can be supported by matching the appropriate Stream class (or third party compression library) to a <a href="https://en.wikipedia.org/wiki/HTTP_compression#Content-Encoding_tokens"><code class="language-plaintext highlighter-rouge">Content-Encoding</code> value</a>.</p> <h2 id="decompressing-a-gzip-request-body-in-servicestack">Decompressing a GZip request body in ServiceStack</h2> <p>Using GZipStream, it isn’t hard to implement a basic ad-hoc decompression routine in a specific ServiceStack endpoint. Have your request DTO implement <code class="language-plaintext highlighter-rouge">IRequiresRequestStream</code> to get access to the raw data, or alternatively the Service class can access this using <code class="language-plaintext highlighter-rouge">Request.InputStream</code>. Feed this to the GZipStream class and the request body is decompressed. You will, however, need to manually deserialize the plaintext data if you are expecting a JSON or XML request, as it’s already too late for ServiceStack to do this automatically. It wasn’t a problem for me as the request I needed to handle was a flat file.</p> <h2 id="implementing-a-general-solution">Implementing a general solution</h2> <p>I figured it should be easy to build this in a generic way so that any <span class="nohyphen">ServiceStack</span> message could be decompressed automatically before it was parsed. However, initially I could not find a way to correctly modify the request body stream in ServiceStack or <span class="nohyphen">ASP.NET</span>.</p> <p>With <a href="https://stackoverflow.com/q/28159280/795339">help from the creator of ServiceStack</a>, I learned that <code class="language-plaintext highlighter-rouge">System.Web.HttpRequest</code> objects have a <a href="https://msdn.microsoft.com/en-us/library/system.web.httprequest.filter(v=vs.110).aspx"><code class="language-plaintext highlighter-rouge">Filter</code> property</a> that allows piping the original input stream of the request through another stream. You can even compose several Filters to modify the request in various ways.</p> <p>Using an HttpModule to apply a Filter to each incoming request, it’s easy to implement automatic decompression of all GZip-compressed request bodies:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Credit to https://stackoverflow.com/a/28159849/795339 public class GZipRequestDecompressingModule : IHttpModule { public void Init(HttpApplication context) { context.BeginRequest += (sender, e) =&gt; { var request = (sender as HttpApplication).Request; string contentEncoding = request.Headers["Content-Encoding"]; if (string.Equals(contentEncoding, "gzip", StringComparison.OrdinalIgnoreCase)) { request.Filter = new GZipStream(request.Filter, CompressionMode.Decompress); request.Headers.Remove("Content-Encoding"); } }; } public void Dispose() { } } // In web.config: &lt;system.webServer&gt; &lt;modules runAllManagedModulesForAllRequests="true"&gt; &lt;add name="AnyUniqueName" type="YourNamespace.GZipRequestDecompressingModule, YourAssembly" preCondition="integratedMode" /&gt; &lt;/modules&gt; &lt;/system.webServer&gt; </code></pre></div></div> <p>By running the decompression during the <code class="language-plaintext highlighter-rouge">BeginRequest</code> event, all ASP.NET and ServiceStack handler code that executes later will behave as if the request was sent in plaintext with no compression at all. Thus, ServiceStack will correctly bind JSON and XML requests, etc.</p> <h2 id="testing-compressed-http-requests">Testing compressed HTTP requests</h2> <p>If you use <a href="https://www.telerik.com/fiddler">Fiddler</a>, a simple script will allow you to send any request with a compressed body. Select the <em>Rules &gt; Customize Rules</em> menu item (Ctrl+R). Add the following near the end of the <code class="language-plaintext highlighter-rouge">OnBeforeRequest</code> function in the file that is opened:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (m_gzipRequest &amp;&amp; oSession.requestBodyBytes != null &amp;&amp; oSession.requestBodyBytes.length &gt; 0 &amp;&amp; !oSession.oRequest.headers["Content-Encoding"]) { oSession.requestBodyBytes = Utilities.GzipCompress(oSession.requestBodyBytes); oSession.oRequest["Content-Length"] = oSession.requestBodyBytes.Length.ToString(); oSession.oRequest["Content-Encoding"] = "gzip"; } </code></pre></div></div> <p>Near the top of the file are several <code class="language-plaintext highlighter-rouge">RulesOption</code> and global variable declarations. Add the following in that area:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public static RulesOption("Apply GZip Encoding to Request") var m_gzipRequest: boolean = false; </code></pre></div></div> <p>Save the file, and the Rules menu in Fiddler should now show an <em>Apply GZip Encoding to Request</em> item. Click that menu item to enable/disable compression. When enabled, any request sent from the Composer view or resent from the Session List will be GZip compressed. The raw compressed data can be viewed/decoded in the Inspector after sending a request, and copying a request into the Composer window will allow directly editing a Base64-encoded version of the compressed data.</p> <h2 id="wrapping-up">Wrapping up</h2> <p>This post summarized everything I know about supporting GZip compressed HTTP requests in a ServiceStack service or ASP.NET application and how to test this using Fiddler. You should now know what a compressed request looks like, how to use GZipStream to decompress data, how to write an HTTP module that automatically handles every compressed request sent to your ASP.NET application or ServiceStack service, and how to add a script to Fiddler to automatically compress any request body. Let me know if there is anything missing or incorrect.</p> Fri, 30 Jan 2015 00:00:00 +0000 https://www.runningcode.net/2015/01/30/request-compression/ https://www.runningcode.net/2015/01/30/request-compression/ Undocumented NSCalendar Methods <p>While working on an iOS app, I found myself writing some NSCalendar extension methods such as <code class="language-plaintext highlighter-rouge">beginningOfDay(relativeToDate:)</code>, thinking that the NSCalendar class was a little lacking. Then I stumbled upon some undocumented NSCalendar methods that accomplish exactly what I was looking for.</p> <p>These methods are not yet mentioned in the latest <a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSCalendar_Class/">iOS</a> and <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSCalendar_Class/">OS X</a> documentation, though they were added to the frameworks as of iOS 8.0 and OS X 10.9. I found them when I command-clicked an another NSCalendar method to check on its signature, and noticed a lot of new convenience methods for constructing and enumerating dates. Check the <a href="https://developer.apple.com/library/ios/releasenotes/General/iOS80APIDiffs/frameworks/Foundation.html">iOS 8 API diffs</a> to see everything that was added. Hopefully this will save someone some effort.</p> <p>Before finding these new APIs, I had finished writing my own version of <code class="language-plaintext highlighter-rouge">startOfDayForDate</code> using TDD. I removed my own extension methods and updated the app to use the builtin methods, but I kept the unit tests that were already written since they function as some nice documentation. I published these unit tests as a <a href="https://gist.github.com/mmertsock/59a7673df76bc31b14e4">Gist</a> in case they prove useful to others.</p> <p><em>Update:</em> <a href="https://nshipster.com/nscalendar-additions/">NSHipster</a> published an in-depth article about all of the recent NSCalendar API additions.</p> Fri, 23 Jan 2015 00:00:00 +0000 https://www.runningcode.net/2015/01/23/undocumented-nscalendar/ https://www.runningcode.net/2015/01/23/undocumented-nscalendar/ MVP Like You Mean It <p><a href="https://en.wikipedia.org/wiki/Minimum_viable_product">Minimum viable product</a> (MVP) is often used in agile development to describe the first major milestone of new software projects. Both at my day job and in my personal projects, we try our best to realize this ideal of shipping a usable product to key stakeholders as soon as possible. But how often do we <em>really</em> accomplish this?</p> <p>MVP can be a challenge to realize with any type of project. At one end of the spectrum, it is tough to find a reasonably small set of core functionality in an enterprise focused application: there are entrenched, complex workflows and business rules to replicate, multiple customers with disparate use cases, and the final vision can be intimidating in its scale. At the other end of the spectrum, when I work alone on some idea for a tool or app, the creative blank slate and lack of external constraints and deadlines can make it hard to find a clear direction and get initial traction: I need to construct everything from scratch, including a vision, market, and audience.</p> <p>Usually, we do arrive at something that can be called an MVP, but I think it typically takes longer to get there than we would want. There is the feeling, in retrospect, that there existed some other set of choices that would have produced a minimum viable product more quickly, with less cost and complexity.</p> <p>This past week, however, I found my way to MVP with a personal project in about eight hours of work. Throw an icon on it and it really could be shipped and used by actual users. It works, it has a basically production-ready database, it handles all the major edge cases and logic needed to function reliably for everyday usage, and it doesn’t look terrible.</p> <p>How did I achieve this? I found one core use case that covers 90% of the time a user will interact with the application, and focused solely on what was essential for the user to efficiently complete this task. I created a “sprint” on my <a href="https://trello.com/mmertsock/recommend">Trello</a> board and give it a title based on this use case. Then I dove right into the implementation and the code was flowing quickly and confidently.</p> <p>The key was that every detail of what I needed to do could fit in my head because the goal was simple and focused. Every unrelated idea was filed as a Trello card and evicted from my mind. I skipped unit tests for views and controllers because at this point the UI was simple to test manually, and it would be highly subject to change after the MVP. For the core business logic, however, I used TDD, as it was clear that getting this right would be vital to the foundation of the entire app. The tests drove the design of the code and kept me focused on the actual user stories. I started using the application myself, for a real-world need, on day one.</p> <p>So, every time you start a new project, take the time to really search for that MVP. It is hiding somewhere in the growing list of ideas, a magical incantation of the right user stories.</p> <p>A good MVP requires focus. It requires <a href="https://www.youtube.com/watch?v=H8eP99neOVs">saying no</a>, or at least “not yet”, to any idea or requirement that competes with your goal. Once you’re there, try to keep the same mindset. The next step is not just checking off a list of features required for 1.0. It is finding the set of use cases that real customers will need for a compelling first release and addressing only those use cases. Address each of these as a series of additional MVPs; try to find the same focus and clarity of purpose. Meanwhile, keep your code shippable and production-ready to minimize the amount of pre-release “productization” and the amount of technical debt to deal with afterward. Get the MVP into the hands of a few users immediately, and be prepared to go agile and change your plans based on their feedback.</p> <p>Keep to this path and you will arrive at the MVAP: the <a href="https://blog.carbonfive.com/introducing-mvap-the-minimum-viable-awesome-product/">minimum viable awesome product</a>. Now, you are ready to ship.</p> Mon, 19 Jan 2015 00:00:00 +0000 https://www.runningcode.net/2015/01/19/mvp-like-you-mean-it/ https://www.runningcode.net/2015/01/19/mvp-like-you-mean-it/ Standing Desk Challenge: Week Four <p>The final week of my participation in the <a href="https://www.runningcode.net/2014/01/07/standing-desk-week-1/">28-day stand up and work challenge</a> has come and gone. Overall it was a great success.</p> <h2 id="observations-week-four">Observations: week four</h2> <p><a href="http://deskhacks.com/challenge-day-16/">Day 16</a>, 192 minutes. To ensure that each day’s goal is met this week, I am committing to a schedule of standing three times each day. <a href="https://www.runningcode.net/2014/01/21/standing-desk-week-3/">Last week</a>, I missed the target on a couple of days by losing track of time.</p> <p><a href="http://deskhacks.com/challenge-day-17/">Day 17</a>, 204 minutes. I wasn’t much aware of bad posture or discomfort from sitting all day long until getting a standing desk. Now, with less sedentary behavior and better awareness and posture, this is the first week where seated periods are truly comfortable, with no more restlessness or discomfort.</p> <p><a href="http://deskhacks.com/challenge-day-18/">Day 18</a>, 216 minutes. Going to want a USB hub to make it easier to move between stations and switch between Windows and Mac machines.</p> <p><a href="http://deskhacks.com/challenge-day-19/">Day 19</a>, 228 minutes. Most standing periods end sooner than I would like them to. After this week, without a schedule to keep, I could probably stand five or six hours per day.</p> <p><a href="http://deskhacks.com/challenge-day-20/">Day 20</a>, 240 minutes. Four hours today was easier and felt more natural than any shorter attempts made just a month ago.</p> <h2 id="how-to-get-started-standing">How to get started standing</h2> <p><em>Start now.</em> The cost of a fancy desk or the prospect of reconfiguring your workday can fuel procrastination: there is always more research and planning to do. Instead, just start standing now—today even. All you need to try it out is a makeshift desk or repurposed surface at approximately the right height. Be creative; there are plenty of DIY standing desk designs out there, from <a href="http://deskhacks.com/give-standing-a-try-today-with-one-of-these-temporary-diy-stand-up-desks/">stacks of office sundries</a> to <a href="http://iamnotaprogrammer.com/Ikea-Standing-desk-for-22-dollars.html">IKEA hacks</a> to permanent retractable wall installations. If you can’t find a convenient way to set up a suitable desk, try standing when you don’t need a desk surface, such as during meetings or phone calls.</p> <p><em>Start small.</em> I am convinced that the incremental approach allows for time to build, refine, and experiment with configurations, techniques, schedules, etc. With small, immediate successes, you will look forward to a bigger challenge each day.</p> <p><em>Use observation and feedback to refine your equipment and posture.</em> Keep a journal. Observe your changes and adaptations. Become aware of bad habits and then watch them disappear over time. Your posture, comfort level, and stamina will improve gradually over a period of weeks, so adjust accordingly. Discuss your setup and experiences with colleagues, or write a blog. If it’s helpful for you, try tools such as <a href="http://www.standingclock.com">Standing Clock</a> to track progress.</p> <p><em>Think beyond your footprint.</em> Once you start to enjoy standing at work, look for ways to improve your comfort and posture during seated periods. For me it was as simple as mixing up sitting and standing times sufficiently as to not get restless when seated. Seek ways to replace sedentary behavior at home with more standing or moving. Consider installing a treadmill (or even a stationary bike) at your standing desk.</p> <p>Good luck with your standing experiment.</p> Tue, 28 Jan 2014 00:00:00 +0000 https://www.runningcode.net/2014/01/28/standing-desk-week-4/ https://www.runningcode.net/2014/01/28/standing-desk-week-4/ Standing Desk Challenge: Week Three <p>This is the third week of my belated participation in the <a href="http://deskhacks.com/how-to-finally-make-the-switch-to-a-standing-desk/">28-Day Stand Up and Work Challenge</a>. Check the entry from <a href="https://www.runningcode.net/2014/01/07/standing-desk-week-1/">week one</a> for an introduction to this series of posts.</p> <h2 id="observations-week-three">Observations: week three</h2> <p><a href="http://deskhacks.com/challenge-day-11/">Day 11</a>, 132 minutes. <a href="https://www.runningcode.net/2014/01/14/standing-desk-week-2/">Last week</a>, I divided standing time into two sessions per day; this week, I split the standing time into three shorter periods, to keep each session under one hour.</p> <p><a href="http://deskhacks.com/challenge-day-12/">Day 12</a>, 144 minutes. With each standing session being under one hour in duration, it’s been over a week since I felt significant soreness or fatigue while standing, and seated periods are broken up enough that they are producing less discomfort.</p> <p><a href="http://deskhacks.com/challenge-day-13/">Day 13</a>, 156 minutes. Lost track of time and missed 20 minutes of standing time. To compensate, I stood at the kitchen table that evening while checking email. We really should be just as conscious about our posture and sitting/standing behaviors in the evening as we are during the work day.</p> <p><a href="http://deskhacks.com/challenge-day-14/">Day 14</a>, 168 minutes. Playing music while standing helps to encourage moving around a little more.</p> <p><a href="http://deskhacks.com/challenge-day-15/">Day 15</a>, 180 minutes. Lost track of time again and missed out on an hour of standing. Being busy with phone calls or urgent work is not an excuse to skip a standing session, especially when it only takes a minute to switch positions.</p> <h3 id="the-value-of-good-peripherals">The value of good peripherals</h3> <p>When using a laptop computer, a standing desk setup clearly requires an external keyboard. I have a <a href="http://www.daskeyboard.com/product/model-s-ultimate/">Das Keyboard, Model S Ultimate</a>. Besides being a general joy to use, I have a theory that it encourages good posture. To personify this keyboard: it <em>wants</em> to be used optimally, two hands on the home row, forearms level, touch typing<sup id="back-1"><a href="#footnote-1">1</a></sup>. Mechanical key switches feel best to me when pressed lightly, at the right angle, with a certain rhythm. For me, the sum of these effects is a straight back and a relaxed pose.</p> <h2 id="next-week">Next week</h2> <p><a href="https://www.runningcode.net/2014/01/28/standing-desk-week-4/">Next week</a>, as the standing sessions reach a total of four hours per day, I want to set a schedule for three standing sessions daily and commit to following that schedule. In the long run, a rigid daily schedule is probably not necessary, but following one for several days may help to instill a routine.</p> <footer class="footnotes"> <ol> <li id="footnote-1"> With no inscriptions on the Model S Ultimate’s keys, cheating is not allowed. <a class="back" href="#back-1">&#8617;</a> </li> </ol> </footer> Tue, 21 Jan 2014 00:00:00 +0000 https://www.runningcode.net/2014/01/21/standing-desk-week-3/ https://www.runningcode.net/2014/01/21/standing-desk-week-3/