<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Adam Leventhal on Medium]]></title>
        <description><![CDATA[Stories by Adam Leventhal on Medium]]></description>
        <link>https://medium.com/@ahl?source=rss-236bcfb1163e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*i23k2o6p3Gh3aMIvTW20cQ.jpeg</url>
            <title>Stories by Adam Leventhal on Medium</title>
            <link>https://medium.com/@ahl?source=rss-236bcfb1163e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 10:12:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ahl/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[AWS Outposts 2021: Stories For Folks Who Love Spreadsheets]]></title>
            <link>https://ahl.medium.com/aws-outposts-2021-stories-for-folks-who-love-spreadsheets-72a2adcfb7b9?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/72a2adcfb7b9</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[outpost]]></category>
            <category><![CDATA[spreadsheets]]></category>
            <category><![CDATA[finance]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Sat, 30 Jan 2021 17:45:29 GMT</pubDate>
            <atom:updated>2021-07-28T19:44:41.129Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uB7ZAa6NArBI0YKHh_K_2w.png" /></figure><p>AWS Outposts is a rack-scale computer that runs on premises. The most recent re:Invent had a bunch of sessions about changes to Outposts. One change that happened without much fanfare is a new lower price (note: LOW-ER, not LOW). <a href="https://medium.com/@ahl/aws-outposts-68e78592c7f8">I looked at Outposts pricing last year shortly after it was released.</a> As with that analysis there are some more stories and oddities hiding in the numbers: customer and sales feedback, trial balloons popped, months-long miscommunications, and — as with any pandemic-era story — a reversion to tedium.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H2GaqIjEtUZxuA58DbWB4w.png" /><figcaption>Pricing changes from 2020 to 2021</figcaption></figure><p>AWS Outposts is an extension of the cloud that lives in your own facilities (either your DC or a colo). It’s a 42U rack that comes with different quantities of EC2 instance types (and therefore different quantities of CPU threads (vCPUs), DRAM, GPUs, and SSD capacity). It’s not a private cloud in that it is tightly bound to the AWS control plane (and a particular AWS region and availability zone). Rather, it’s a little chunk of AWS physically close to your other systems.</p><p>All the analysis below is grounded on calculations you can find in this spreadsheet.</p><p><a href="https://docs.google.com/spreadsheets/d/1UWtXpdIiw5UYXpqOOo3QC3uaNd30Ngaua9EwEfzYtMU/edit#gid=0">AWS Outpost comparison 2021Q1</a></p><h3>Margin Call</h3><p>In last year’s analysis we saw that the cover charge for an Outpost rack was about $200k before you start adding useful components like CPUs and DRAM. That’s for the rack, the power, the switch, fans.. other stuff? Wheels? In case that doesn’t sound crazy: it is. Deeply. The margin on those components must have been astronomical. Maybe some customers noticed and rebelled.</p><p>Outposts come in several flavors that support different EC2 instance types. The price of each model is a simple linear combination of the component instances whose prices we can derive. The new price of the rack is exactly $80k — slightly more reasonable than it was last year.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6Msw1FYkDkUNVy9FTsP0GQ.png" /></figure><p>The price reduction of the rack by $121,651.20 across all models accounts for all price changes except for 3: one reasonable, one kind of annoying, and one goofy.</p><h3>No dev unit stimulus</h3><p>A year ago the cheapest dev unit (actually two models, distinguished only by their external network type) had a special discount. Each AWS Output model is advertised as supporting various EC2 instance types. Using some algebra (or Excel Solver) I calculated the price of each instance type in Outposts. Most are priced similarly to their (3 year reserved instance) cloud-operated EC2 analogs (±8%). Last year, however, the m5.12xlarge came at an 80% discount, but that instance type only came in the cheapest dev unit, making it — ostensibly — an attractive model to start with and kick the tires.</p><p>No more! In 2021, the m5.12xlarge is no longer an aberration (see above). The dev unit is still cheaper than it was a year ago, but only by $56k (not $121k) as it was — effectively — already discounted by $65k. The Jassy giveth and the Jassy taketh away. This also means it is no longer the cheapest model — and it never should have been since other dev models with fewer vCPUs and less DRAM were previously more expensive.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TgEpb7YeLqW1R0OMiHRg6Q.png" /><figcaption>Dev unit pricing has changed a bit</figcaption></figure><p>The smallest dev unit comes with 96 threads and 192GB of DRAM. Even with the new low (low!) price of $118k, that seems … exploitative. A comparable server (singular) from Dell is about 1/5th of that price.</p><p>Note that of the instance types that appear in Outposts, the m5.12xlarge was the only one to see a significant price reduction when comparing the 3 year reserved instance price. I was pretty surprised by this. AWS epitomizes the intersection of relentless capitalism and steady technical advancement. Weren’t we promised cheaper and cheaper computing in the cloud? Where’s my Moore’s Law dividend? As with so many incremental benefits, they’re going disproportionately to the relentless capitalist. More on this later, but here’s exhibit A on why the future isn’t going to be one where Jeff Bezos owns and operates every computer on the planet.</p><h3>Price Correction</h3><p>The OR-LR4DI6P is (with an asterisk) the most significantly discounted Outpost model. It supports a smattering of instance types tallying to 1152 vCPUs and 5TB of DRAM. The good news is that its price dropped $40k more than any other; the bad news is that it was previously overpriced by $40k. Last year I called this guy out as an anomaly. Was it overpriced to attempt to reduce the number of models? That seems weird for — what was a year ago — a new product line. Was it a divining rod for finding wells of dumb customers? Either away the experiment seems to be over.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*awsu1DLpIKV8JsmiscVBvw.png" /></figure><p>The other short-lived experiment was the OR-M6GJ8FE which has been EOLed. With 2 x g4dn.12xlarge and 4 x m5.24xlarge, this was the only dev unit to include a GPU-enabled instance. It was also — by far — the most overpriced model, 21% / $125k more expensive than it should have been based on the per-instance type unit prices. It was hard to imagine a scenario where it should not have upgraded to the OR-H7KSNNO — $90k more for 2 extra g4dn.12xlarge and 3 extra m5.24xlarge. It’s been replaced, more or less, with the OR-LMF5LJ1 which just has 2 x g4dn.12xlarge for $161k.</p><h3>Comedy of errors</h3><p>The largest nominal price reduction was for the OR-HUZEI16 with 8 x m5.24xlarge and 4 x c5.24xlarge (1152 vCPUs and 3840GB of DRAM). This guy dropped by $200k from $791,586.16 to $590,216.36.</p><p>The weird thing is that nothing changed in the unit math. A year ago it was priced exactly equal to the sum of its parts; same story this year. What changed, it turns out, was its parts. A year ago, those 4 x c5.24xlarge instances were 4 x r5.24xlarge; c5 means “half the memory of the m5” and r5 means “double the memory of the m5”. Last year that higher price applied to a unit with an extra 2304GB of DRAM.</p><p>Looking back at my data from a year ago I now see that difference sources were inconsistent. The <a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit#gid=2020069042&amp;range=C18">website</a> said r5 while the <a href="https://aws.amazon.com/blogs/aws/aws-outposts-now-available-order-your-racks-today/">console</a> (and <a href="https://gist.github.com/ahl/5820dc0def00063b883c73e9e131c287#file-outposts-json-L184">API</a>) said c5.</p><p>There must have been a transcription error somewhere; clearly the quants in the pricing team were looking at the website (r5) and not the console (c5). Someone figured that out in the past year. I like to think it was a customer who went looking for their missing 2TB of DRAM.</p><h3>Amazon Financial Services</h3><p>A quick note on the financing option. Outposts have three ways to buy: cash upfront, monthly installments, or monthly installments with 50% down. This is typical of other long term commitments in AWS. Check out the financing options in EC2:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZzL-SA1W2BVfb0ffytL8dw.png" /><figcaption>That’s what I call a cluster!</figcaption></figure><p>The terms are all within epsilon of 8.5% with half the money down, and 9.2% with $0 down.</p><p>In contrast, the 2019 rates for Outposts were as wild as you get in the office of the CFO:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7ij9KfhkAXFohZFnj0dWQQ.png" /><figcaption>Break out your D20 — it’s time to do some pricing!</figcaption></figure><p>Clearly someone put an end to that fun in 2020. The new rates are precisely 8.496% and 9.247%. No exceptions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Zw3lWQ3yHGjLAUef7DnRZg.png" /><figcaption>It <strong><em>is </em></strong><em>color coded…</em></figcaption></figure><h3>(Smaller) Component Pricing</h3><p>Last year I just looked at the prices of each EC2 instance types that make up the Outpost models. I’ve spent the past year a lot closer to computer components so broke it down further, looking at the per vCPU (thread), GB of DRAM, GPU (an NVIDIA T4), and the SSDs. How? Excel’s Solver function. It’s seriously life-changing. I set up the inputs (the component prices) and then wrote formulas multiplying the price of each component by the number in each model to get the expected price per model. Solver will maximize or minimize a single value so I wrote a formula that sums the absolute values of the differences between actual and computed cost for each Outpost model. A few iterations of Solver later and I had this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AuWBkuXMC4nPP2xl5dzSAw.png" /><figcaption>Find a CPU that costs more than $300 per thread. I’ll wait.</figcaption></figure><p>AWS hasn’t announced the pricing for their upcoming <a href="https://aws.amazon.com/outposts/1u-2u/#Features">2U Outpost</a> model, but from this we can take a pretty good guess (the 1U will use a Graviton processor so we don’t have data to extrapolate from for that model). 128 vCPUs, 512GB of DRAM, 8TB of SSD, plus some extra for a slimmed down chassis, I’d expect it to be something like $75k. Which is nuts.</p><p>Maybe AWS will surprise us, but I’m not holding my breath. Outposts are not just expensive but significantly overpriced — despite the big drop and by quite a bit.</p><p>Take this next bit with a big grain of salt, but I picked some parts off the Dell website that seemed like fair comparisons. There might be more expensive parts in Outposts and I’m not accounting for all the other stuff that goes into a server, but CPU/DRAM/GPU/SSD are going to dominate everything else. Outposts pricing remains at least mildly rapacious.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MT4BX7II94GTGrexZVrLrw.png" /></figure><h3>Conclusion</h3><p>I understand why customers want Outposts. Despite the grousing in this post, I’m an AWS fan and have tremendous respect for their rate of innovation. Customers want API-driven infrastructure, the flexibility and speed of development that it provides. Traditional on-premises vendors, most concisely represented by the EMC/Dell/VMware conglomerate, have failed to evolve their way into this experience which is why the hardware and software from the hyperscalers (including Outposts) looks quite different. Customers are faced with a literal dilemma: on one hand they can continue buying the moderately priced, expensive to operate, inefficient to use, legacy gear from Dell/HPE/etc or they can pay exorbitantly for something like AWS Outposts. The former is throwing good money after bad, investing in an ecosystem that has continued to underdeliver; the latter cedes more and more control to AWS and locks them into an unaffordable future.</p><p>Shortly after writing my first blog post on this topic a year ago, I joined <a href="https://oxide.computer/">Oxide</a> where we’re building a computer that combines the hardware and software lessons from the cloud with sensible economics. It’s exciting building this computer the world needs.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=72a2adcfb7b9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An eBay Addiction, a Logic Analyzer, and a VR Game]]></title>
            <link>https://ahl.medium.com/an-ebay-addiction-a-logic-analyzer-and-a-vr-game-861b894338a6?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/861b894338a6</guid>
            <category><![CDATA[raspberry-pi]]></category>
            <category><![CDATA[makers]]></category>
            <category><![CDATA[arduino]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Mon, 30 Dec 2019 16:31:01 GMT</pubDate>
            <atom:updated>2024-01-31T18:03:11.257Z</atom:updated>
            <content:encoded><![CDATA[<h3>An eBay Addiction, a Logic Analyzer, and a VR Game: How I Made Some Christmas Decorations Dance</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U4SbU_iwg4sO1aflC-ZbVw.jpeg" /></figure><p>This was kind of a silly project. If I had known it would end with a bunch of wholesome Christmas decorations dancing along to Lizzo I probably would have done it a long time ago. Here’s the story of how I took some beloved kitsch, a logic analyzer, sed, awk, Google Sheets, Python, an FT232H, some wires, data from a VR game, and a few evenings to make it happen. (Just want the punchline? Skip to the end.)</p><h3>Happy Tapper eBay addiction</h3><p>My family has a fair bit of music-producing stuff from Hallmark: books, stuffed animals, and, of course, holiday decorations. Chalk it up to an enthusiastic grandparent. The Happy Tappers are a favorite. We started with two — a reindeer and a snowman — and I was tasked with filling out the full set of four. We now have 13, so mission accomplished and then some.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s4GsDlkEpUQNiMii3OMEfw.jpeg" /><figcaption>Some debate in my family about whose toys these are…</figcaption></figure><p>Each Tapper plays two songs and dances; you can plug in other Tappers via ports on either side and they become backup dancers. Deeply alluring, clearly. I got a good deal on Santa, then a better deal on a different Santa. Then there was a great deal on a set of 4, so had to get it. Then — after months of waiting — I pounced on a set of 5, including the very rare “painter” elf. So, in short: I have a problem. This solstice holiday season my plan was to figure out how they work and get them to do some out-of-spec dancing.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FuUPnrAW0AUY%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DuUPnrAW0AUY&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FuUPnrAW0AUY%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/18d6bbe506bc7d81eb755e12773ed7b6/href">https://medium.com/media/18d6bbe506bc7d81eb755e12773ed7b6/href</a></iframe><h3>Vivisection</h3><p>My first step was to pull one apart. There’s a microcontroller (covered with scratched up epoxy) that controls the speaker and electromagnet actuators while sending and receiving signals over the bus formed by connecting multiple Tappers (henceforth, the Dance Bus).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q0v8iWFeZn5ZHtNstdNq_Q.jpeg" /><figcaption>Santa’s (X5053) brain</figcaption></figure><p>Examination with a multimeter showed that pin 3 is ground, pin 2 is for dance signals, and pin 1 is high when one of the Tappers on the Dance Bus starts playing. I tried sending some pulses down pin 2, but that didn’t produce any tapping (happy or otherwise). So I hooked one up to my brand new logic analyzer.</p><h3>Reverse Choreography</h3><p>You can spend a lot for a great logic analyzer which is what I didn’t do. I had never used a logic analyzer of any kind until a few weeks ago when I took a <a href="https://hardwaresecurity.training/trainings/">hardware hacking class</a>. Basically as a party favor for the class, we were each given a <a href="https://1bitsquared.com/collections/all-products/products/bitmagic-basic">BitMagic logic analyzer</a> ($30) (worse, by the way, than the fancy <a href="https://www.saleae.com/">Saleae</a> we used during the class). I used PulseView, the open source front-end, to gather and examine signal data. <a href="https://sigrok.org/doc/pulseview/unstable/manual.html">The docs</a> were great; it was an testament to the power of open.</p><p>I hooked up the leads to the target Tapper, pushed the button, and… nothing. Its button didn’t work. Whether an artifact of the cheap logic analyzer or due to some pilot error, the logic analyzer was actually sending a signal — 3V measured between pins 1 and 3 — which means on the Dance Bus that a Tapper is active and others should expect to receive signals. I worked around this by pushing the Tapper’s button and then quickly hooking up the leads.</p><p>Here’s 15 seconds worth of Dance Bus activity in PulseView:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sN5RsbICtReYWG0tqIpv8A.png" /></figure><p>Each of those little blips corresponds to a message on the Dance Bus. Zooming in we can see a couple of the messages:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QwK1tcpnrx7npaYciYQDzg.png" /></figure><p>The cursor tool lets you measure the time between features:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xLZtaCRBfuVMGciCRXJN7Q.png" /></figure><p>I measured a few by hand, but it was going to be tedious to look at each signal that way. I needed the data in a form I could examine programmatically. PulseView has a bunch of output formats; the one I landed on (by trying them all) is “Value Change Data” whose output looks like this:</p><pre>$date Thu Dec 19 23:17:09 2019 $end<br>$version libsigrok 0.6.0-git-ef62ab6 $end<br>$comment<br>Acquisition with 1/8 channels at 1 MHz<br>$end<br>$timescale 1 us $end<br>$scope module libsigrok $end<br>$var wire 1 ! D0 $end<br>$upscope $end<br>$enddefinitions $end<br>#0 1!<br>#1291266 0!<br>#1309128 1!<br>#1311126 0!<br>#1313143 1!<br>#1315111 0!<br>#1319103 1!<br>#1321095 0!<br>#1323096 1!<br>#1325115 0!<br>...</pre><p>Microsecond timestamps (noted in the header; perhaps due to the 1 MHz sampling frequency I used) and values at the time of changes (Value Change Data — in retrospect: duh). I stripped out the header and line prefixes with sed, then computed deltas between timestamps with awk:</p><pre>$ cat xx.vcd | sed -n &#39;s/^#\(.*\)/\1/p&#39; | awk &#39;NR&gt;1{ print $1-last } { last=$1 }&#39;<br>1291266<br>17862<br>1998<br>2017<br>1968<br>3992<br>1992<br>2001<br>2019<br>2002<br>289808<br>17531<br>1989<br>2010<br>1962<br>3978<br>1984<br>1995<br>2003<br>3958</pre><p>The longest signals I care about appear to be no more than 18ms so I used some more awk to convert &gt;20ms durations to newlines and everything else to CSV:</p><pre>$ cat xx.vcd | sed -n &#39;s/^#\(.*\)/\1/p&#39; | awk &#39;NR&gt;1{ print $1-last } {last=$1 }&#39; | awk &#39;{ if ($1 &lt; 20000) printf &quot;%s,&quot;, $1; else print &quot;&quot;; }&#39;<br>17862,1998,2017,1968,3992,1992,2001,2019,2002,<br>17531,1989,2010,1962,3978,1984,1995,2003,3958,<br>17811,1995,1999,1997,4020,1964,1999,1993,2005,<br>17631,1996,2020,1967,3995,1991,2003,2012,3973,<br>17676,1986,1995,2004,3953,1983,1990,1987,2013,<br>17765,2008,1993,1956,3978,1989,2011,1960,3979,<br>...<br>17788,1995,2001,2016,3970,1993,1999,1997,2022,<br>17959,1966,1998,1992,4018,1968,1999,1994,4003,<br>17525,1989,2009,1961,3977,1985,1994,2005,1997,<br>17612,1965,1997,1991,3998,2012,2001,1963,3993,<br>17879,1963,1990,1983,3981,2005,1994,1956,1992,<br>17782,1993,1998,1996,4015,1963,2000,1990,4018,<br>17968,1970,1997,1992,3998,2013,2001,1964,2002,81,2,</pre><p>Scanning vertically, the numbers looked close enough that felt like I might be onto something, so I threw them into a <a href="https://docs.google.com/spreadsheets/d/1U8YG2iLemHUOoMn0lv56K9RbTZK-YdoSblHdrBGJE8M/edit?usp=sharing">spreadsheet</a> to verify (<a href="https://blog.usejournal.com/aws-outposts-68e78592c7f8?">I ❤️ spreadsheets</a>):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aNyqfMzr4fEf8rs-26dJNg.png" /></figure><p>The signals looked pretty consistent until the last segment, 9 pulses of the following durations: 17.7ms, 2ms, 2ms, 2ms, 4ms, 2ms, 2ms, 2ms, ???. I split the last pulse into subsets above and below the average to get a pretty clear bimodal distribution, one right at 2ms and the other at 4ms:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c_X6G9z9nBH4ZifSwlPsFw.png" /></figure><p>Two signals for the two feet of each Tapper on the Dance Bus. I suppose I could have guessed that, but based on the preprogrammed dances I expected a single signal that just cycled from foot to foot. Now that I knew how the Tappers communicated to each other, I had to generate those signals myself.</p><h3>Tapping Out Signals</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/970/0*O3oEJ2v9y0B5vMvN.jpg" /><figcaption>Every kid wants an FT232H in their stocking</figcaption></figure><p>Back when I was a kid (in the days before our serial buses were universal) PCs had a <a href="https://en.wikipedia.org/wiki/Parallel_port">parallel port</a>. You could hook up printers and other peripherals (like Zip Drives and slow-ass network devices), but with the tiniest bit of ingenuity you could write code to turn certain pins on and off. We used them to do really cool stuff like control LEDs… and control other LEDs. The possibilities were endless.</p><p>Another party favor from that hardware hacking class was an Adafruit FT232H Breakout ($15) which Limor Fried (founder of Adafruit) <a href="https://www.youtube.com/watch?t=965&amp;v=CYBi0CEoR1A">called the “modern parallel port”</a>. It’s a USB bridge that you can use to speak useful, well-known protocols like SPI and I2C, or — in our case — less-useful, dance protocols via general purpose input/output (GPIO) pins. This thing is super cool: extremely approachable, easy to use, no IDE. It’s a great starting point for anyone who wants to write software that can control simple electronics.</p><p>The Tappers are ~4.5V (3 x AAA batteries) and the FT232H outputs 3V signals, but it’s what I had so I went with it. Adafruit operates mostly in Python so I went with that too. Because I was paranoid about the resolution of time.sleep() I decided to spin for the allotted time which resulted in this code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/77e0d4fc0853b80a625d86acc7e163cf/href">https://medium.com/media/77e0d4fc0853b80a625d86acc7e163cf/href</a></iframe><p>And I tacked on a little interactive test program:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c7246c6ac08ca8d0233e06af98f9b60/href">https://medium.com/media/2c7246c6ac08ca8d0233e06af98f9b60/href</a></iframe><p>Full of anticipation, I plugged in a Tapper expecting to j-k-j-k a little two-step and… nothing. So I hooked up the logic analyzer again, this time to my FT232H to see if it was producing the signals I expected. Here’s what I got:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aFHeF1EpGwNDsgQzNU1UmA.png" /><figcaption>That first signal should be 17.7ms but is only 2ms</figcaption></figure><p>The first low pulse that was supposed to be 17.7ms was only around 2ms. I thought a bit about the many sources of latency that could cause this, but then opted for urgency over curiosity, and added another 15.7 to that first value. I measured again and things looked good, so I hooked up the Tapper. Lo and behold, he started tapping:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FXF2ZmL9MKeA%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DXF2ZmL9MKeA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FXF2ZmL9MKeA%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/4bcc79b6d4a457beabc3150b5555f296/href">https://medium.com/media/4bcc79b6d4a457beabc3150b5555f296/href</a></iframe><p>I could move their feet now I needed to make them dance.</p><h3>Tap Tap Revolution</h3><p>I knew I couldn’t outtap the rhythmic geniuses from Hallmark, but I could tap into one of the great breakthroughs of our era: rhythm games. The greatest of these — in my completely unqualified opinions — is indisputably <a href="https://beatsaber.com/">Beat Saber</a>. It’s the killer app for VR. I’m not going to explain; just check it out:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F-U6qPmxW0Cs%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D-U6qPmxW0Cs&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F-U6qPmxW0Cs%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4036cab43d90856a8dd917ce6848108/href">https://medium.com/media/b4036cab43d90856a8dd917ce6848108/href</a></iframe><p>Beyond just the songs arranged by the developer, there’s an awesome community of content creators that make new songs available for download on <a href="https://beatsaver.com/">BeatSaver</a> (and other places I infer). The file format is pretty simple. You can <a href="https://github.com/ahl/tapsaber/blob/master/tapsaber.py">check out the code</a> if you want the details, but eventually you’ll poke deep enough in a JSON file to find an array of objects that look like this:</p><pre>{<br>  &quot;_time&quot;: 51.875,<br>  &quot;_lineIndex&quot;: 1,<br>  &quot;_lineLayer&quot;: 0,<br>  &quot;_type&quot;: 0,<br>  &quot;_cutDirection&quot;: 7<br>}</pre><p>Time is in units of beats (we get BPM elsewhere); index and layer indicate the location of the block to hit; cut direction is pretty self-explanatory; type indicates which hand you need to hit the block with. I ignored location and direction, and just used time and type. With some simple Python, I unpacked the file format, played the music, and fed the tap data into my Tappers. Here’s the result:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FFCaSSJGif8g%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DFCaSSJGif8g&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FFCaSSJGif8g%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c14329e745fab53e5b9a466817d6f7b7/href">https://medium.com/media/c14329e745fab53e5b9a466817d6f7b7/href</a></iframe><p>You can plug in more Tappers to the Dance Bus so without further ado, Truth Hurts like you’ve never seen it before:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FD0gZBMjndwc%3Ffeature%3Doembed&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DD0gZBMjndwc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FD0gZBMjndwc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/ffa3bbf06e978ce5de7c0406e8f5acf0/href">https://medium.com/media/ffa3bbf06e978ce5de7c0406e8f5acf0/href</a></iframe><p>There were a bunch of moving parts; I was stoked (and surprised) to see it all come together. It turns out not to be that hard, and the parts weren’t that expensive. Give it a shot; just don’t outbid me on eBay.</p><p>Parts List:</p><ul><li>Happy Tappers (available on eBay)</li><li><a href="https://1bitsquared.com/collections/all-products/products/bitmagic-basic">BitMagic Basic Logic Analyzer from 1 Bit Squared</a></li><li><a href="https://sigrok.org/wiki/Downloads">PulseView</a></li><li><a href="https://www.adafruit.com/product/2264">Adafruit FT232H Breakout — General Purpose USB to GPIO+SPI+I2C</a></li><li>You’ll need some sort of soldering iron for the FT232H; I used my one from RadioShack in 1995</li><li><a href="https://www.amazon.com/gp/product/B01N9QW620">Etekcity MSR-R500 Digital Multimeter</a> (no endorsement other than it being cheap and available on Prime)</li><li><a href="https://www.amazon.com/gp/product/B00LSG5BJK">HiLetgo 3pcs 400 Ties Mini Solderless Breadboard</a> (not strictly necessary)</li><li><a href="https://www.amazon.com/gp/product/B07GD2BWPY">EDGELEC 120pcs Breadboard Jumper Wires</a></li><li>Song data from <a href="https://beatsaver.com/">BeatSaver</a></li></ul><p>Optional:</p><ul><li><a href="https://www.oculus.com/quest">Oculus Quest</a> / HTC Vive + a gaming PC</li><li><a href="https://beatsaber.com/">Beat Saber</a></li><li>Lots of time</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=861b894338a6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AWS Outposts by the numbers]]></title>
            <link>https://ahl.medium.com/aws-outposts-68e78592c7f8?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/68e78592c7f8</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[finance]]></category>
            <category><![CDATA[outpost]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Sun, 15 Dec 2019 01:06:44 GMT</pubDate>
            <atom:updated>2020-01-16T05:03:45.392Z</atom:updated>
            <content:encoded><![CDATA[<h3>AWS Outposts by the Numbers: A Far-Too-Deep Dive Into Pricing</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*h8t6AIDKogg4q927PDWQnQ.png" /></figure><p>AWS announced Outpost a year ago. It’s a rack-scale computer that lets customers run a limited set of AWS services on-premises. A couple of weeks ago, AWS <a href="https://aws.amazon.com/blogs/aws/aws-outposts-now-available-order-your-racks-today/">announced</a> that Outpost was out of beta and generally available.</p><p>Now that the pricing is public, I ripped apart the data. These are big, expensive boxes; what do you get for the money? How does it compare to running instances in the cloud?</p><p>What follows is the analysis I wanted, but couldn’t find. If you’ve been thinking “I wish I had a spreadsheet to get a deep understanding of Outpost pricing”, you’ve come to the right place. If you think spreadsheets are tedious, or perhaps a bizarre thing for a person to do in his idle time, spoiler: I’m going to get really excited about an Excel optimizer, conditionally color some cells, and use RATE() for the first time. So, yeah, it’s going to be wild / I won’t be offended if you leave after watching the “is it on-premise or on-premises?” supercut.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FRWHocP0AzMc%3Ffeature%3Doembed%26cc_load_policy%3D1&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRWHocP0AzMc&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FRWHocP0AzMc%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/883c0cc50a96861c0815aa0506403e5e/href">https://medium.com/media/883c0cc50a96861c0815aa0506403e5e/href</a></iframe><p>Here’s the google sheet with all raw data and analysis:</p><p><a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit?usp=sharing">AWS Outpost comparison 2020Q1</a></p><h3>Data Gathering</h3><p>We can see all the data: price, instance types, etc. But which data? On the morning of December 10th the data looked like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4EFjCMFaw17A15--6ISNHg.png" /></figure><p>That afternoon, it looked like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bhFemxFjrAWK23ws6m8ZCw.png" /></figure><p>Every instance type had its count incremented by one e.g. the first one, OR-R2XVM9Q, had 1 m5.24xlarge, c5.24xlarge, and r5.24xlarge and the later grab shows 2 of each.</p><p>I decomposed the configuration strings into a count of instances types for each Outpost configuration, and then plugged them into Excel’s Solver to figure out the Outpost pricing model. The numbers didn’t converge to a rational answer with the old data, but it did with the new data. So I’m sticking with the new data… even though the data in the AWS console (and the <a href="https://gist.github.com/ahl/5820dc0def00063b883c73e9e131c287">JSON data</a>) still match the old data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HbdYegrDNbfznWivFzZCAw.png" /></figure><h3>Not an ARM</h3><p>No surprise: <a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit#gid=0">these suckers ain’t cheap</a>. A quarter million for “Development &amp; testing units” up to $900k for one chock-a-block full of memory. As with other term commitments from AWS, you can choose to pay all up front, pay in monthly installments, or pay a 50% downpayment with smaller monthly installments. Effectively Amazon lets you take out a mortgage on your outpost at around 7-8% — 0.7% less if you put 50% down.</p><p>The were notable outliers: the dev/test models with either a single c5.24xlarge or m5.24xlarge both had an interest rate closer to 4%. It makes sense that Amazon might want to make it a bit less painful to get that first evaluation Outpost in your lab, but it’s weird that that discount only applies to 2 of the 5 dev test models.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0ZlWInFsodTkrOxOr1wSuw.png" /><figcaption>The RATE() function is my new best friend</figcaption></figure><h3>Outpost vs. Inpost</h3><p>How do Outposts compare to the cost of running workloads in AWS? For this, I used reserve instance pricing with a 3 year term. I figure if you’re ready to drop some serious <a href="http://rack.urbanup.com/10983054">racks</a> on this gear, a 3 year commitment probably doesn’t scare you. For this comparison I made the nominally gross simplification of ignoring storage (since that seems to have a consumption-based monthly component), power (a large and growing cost center), as well as other service charges (more on that in a bit).</p><p>For each Outpost configuration, I decomposed it into a count of the included instance types. I figured each Outpost probably had a fixed cost component (the rack itself, ToR, etc.) and then you’re just paying a fixed cost for each instance type. I mentioned Excel Solver earlier — it’s a brilliant tool to have at the ready, <a href="https://support.office.com/en-us/article/define-and-solve-a-problem-by-using-solver-5d1a388f-079d-43ac-a7eb-f63e45925040">go learn it if you haven’t already</a>— on a spreadsheet with formulas, it modifies values (the Outpost cost for each instance type) and optimizes for a goal value (minimizing the delta between the computed and known cost for Outpost configurations). With the first set of data I pulled off the Outpost pricing page, the numbers weren’t converging at all, but with the new data they converged right away. In fact, it was pretty straightforward <a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit#gid=784839167">to do by hand</a> with simple operations like this:</p><blockquote>m5.24xlarge<br>= 12 m5.24xlarge ($738,3141) - m5.24xlarge ($246,373) / 11<br>= $44,722</blockquote><p>Now that we have the pricing model for Outpost, we can <a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit#gid=1454517013">compare</a> it to the prices for the corresponding cloud-hosted instance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/860/1*4LjxEHbkCtXoAJWwQj8zug.png" /><figcaption>Friends and family discounts on the m5.12xlarge</figcaption></figure><p>Most of them are close-ish. The obvious outlier is the m5.12xlarge instance type. This only appears in the two least expensive (dev/test) configs. So — as before — it might make sense that Amazon is trying to create financial incentives for that first development box.</p><p>The pricing model with values for the chassis and each instance type produces an <a href="https://docs.google.com/spreadsheets/d/1afmhB3UmMjWbb3QDH8QZh8-7A8xKA-xF0WvX3Oza1BE/edit#gid=2137790034">expected price for each config</a>. Only two aren’t spot on: a colossal $800k / 5TB memory config which is about 5% more expensive than we’d expect, and the GPU-enabled dev/test config which is a whopping 20% overpriced.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zjknc8SvZOSEvz6A_BVcEQ.png" /></figure><p>The 5% lift on the “Mixed capacity large unit” is hard to figure. Maybe they wanted to push people to the 6TB model which is only $70 (dollars — not kilo-dollars) more (aka two weeks of running an on-demand m5.24xlarge instance).</p><p>The dev/test “Graphics medium unit” is the only dev/test unit with a GPU (8 actually). It’s also the only dev/test that doesn’t have some sort of price incentive (on the contrary). On a relative basis it’s by far the most expensive Outpost config, and it’s hard to imagine why someone would opt for it over the “Graphics large unit”. For $90k more (15%) you go from 2 to 4 GPU-enabled instances and from 4 to 7 m5.24large. In short: weird price; it’s either an oversight or a story.</p><h3>Off Premise</h3><p>Now that you’ve paid for it, wheeled it into your data center, and powered it, you’re good to go right? Sort of. You pay for the Outpost and then you keep paying by the hour. EBS is $0.15/GB-mo and up. Apparently the meter’s also running ECS and EKS, but the Outpost pricing page sends you to the ECS/EKS pricing pages which point back to the Outpost pricing page, so TBD on that. The managed database service (RDS) is available in Outposts, but it’s pretty expensive. For example, if you want to run MySQL on one of those m5.24xlarge instances in an Outpost that’s an $3.60/hr or $94,608 <strong>on top of</strong> the however many hundred thousand for the gear. Compare that to the 3 year, upfront cost, all-in cost to run that same database in AWS of $86,671 (no additional charge from EC2).</p><h3>Cloud Migration vs. Mitigation</h3><p>An Outpost isn’t a hyperscaler-type computer you own and operate. It’s aptly named for what it is: an extension of AWS (indeed, linked to a particular region) with a limited set of services that sits on-premise in close proximity to other systems you might care about. Outposts meet a real need; I expect there’s an even larger need to build true private clouds with the efficiency of the hyperscalers.</p><p>The top tier Outpost comes with 576 cores (1152 vCPUs) and 9.2TB of DRAM for $900k. There’s margin in there for a system that meets the private cloud demand without falling farther down the AWS gravity well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j7z1kRBV5SSMqXTeeZ3IxQ.png" /><figcaption>Two day delivery with Prime… I assume</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=68e78592c7f8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Slack Workflow Builder: the Good, the Bad, and the Broken]]></title>
            <link>https://ahl.medium.com/slack-workflow-builder-the-good-the-bad-and-the-broken-e9f7c3ddbe3e?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/e9f7c3ddbe3e</guid>
            <category><![CDATA[slack]]></category>
            <category><![CDATA[workflow-builder]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Sun, 20 Oct 2019 15:26:01 GMT</pubDate>
            <atom:updated>2019-10-20T15:26:01.675Z</atom:updated>
            <content:encoded><![CDATA[<p>Slack’s been <a href="https://slackhq.com/new-slack-features-boost-alignment-productivity">hyping</a> Workflow Builder since April; <a href="https://slackhq.com/automate-tasks-in-slack-with-workflow-builder">now that it’s out</a>, I wanted to see what it can do, figure out how it works, void some warrantees, and try to break it. Not all of my workspaces had workflows enabled, but I found one, and started building workflows.</p><p>The first choice you make when creating a new workflow is how it will be triggered. Presumably there will be more options in the future; right now it’s just three: when a user picks it from a menu of workflows (like a macro), when a new user joins a specific channel, or when a user reacts with an emoji. Curiously, you can’t react with a <a href="https://sfctoclub.slack.com/apps/collection/actions">message action</a> (the … on each message) which seemed like the preferred trigger over emojis — future work I suppose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hhq5Y2G2srRkomRELAT6DQ.png" /><figcaption>Bonus points if you can even find the “Actions menu”</figcaption></figure><p>Once you’ve picked the event source, you specify a sequence of steps. There you have even fewer choices: send a message or post a form.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ar4X4KAvYXLuNxswAa93mg.png" /><figcaption>We have both kinds of wine… white and red.</figcaption></figure><p>What’s pretty cool is that both the initial event and subsequent steps produce output values that subsequent steps can reference. For example, you can create a step that posts a message that @mentions the person who triggered the workflow, or you could send a message to a different user that includes a value from a form someone filled out. It’s pretty slick and despite the limited range of actions you can take today, it’s easy to imagine how Slack might extend this in the future with more action types and more outputs.</p><p>And that’s really it. It’s like a poor man’s Zapier or IFTTT. Destitute maybe. But you can see the outline of how this could expand: more event sources, more steps, and maybe more complex, branching workflows.</p><h3>Teardown</h3><p>The basic hypothesis of workflows is that non-technical users need a visual builder. Fortunately, it’s all represented as a json object that we can download. The overall structure looks like this:</p><pre>{<br>  &quot;source_id&quot;: &quot;&lt;looks like some unique timer-based id&gt;&quot;,<br>  &quot;version&quot;: &quot;1&quot;,<br>  &quot;workflow&quot;: {<br>    &quot;name&quot;: &quot;name of the workflow<br>    &quot;blueprint&quot;: {<br>      &quot;version&quot;: &quot;1&quot;,<br>      &quot;trigger&quot;: {<br>        &quot;type&quot;:<br>          &quot;reaction_added | channel_action | member_joined_channel&quot;,<br>        &quot;id&quot;: &quot;&lt;uuid&gt;&quot;,<br>        &quot;config&quot;: {<br>           ... trigger-specific data ...<br>        }<br>      },<br>      &quot;steps&quot;: [<br>        {<br>          &quot;type&quot;: &quot;dialog | message&quot;,<br>          &quot;id&quot;: &quot;&lt;uuid&gt;&quot;,<br>          &quot;config&quot;: {<br>            ... step-specific data ...<br>          }<br>        },<br>        ...<br>      ]<br>    }<br>  }<br>}</pre><p>Triggers and steps have an id and type; the type defines the contents of the config member. The id in the trigger and each step be referenced in later steps to pull out certain info. For example, a message step looks like this:</p><pre>&quot;type&quot;: &quot;message&quot;,<br>  &quot;id&quot;: &quot;&lt;uuid&gt;&quot;,<br>  &quot;config&quot;: {<br>    &quot;user&quot;: {<br>      &quot;ref&quot;: &quot;&lt;uuid of trigger&gt;==user&quot;<br>    },<br>    &quot;has_button&quot;: false,<br>    &quot;message_text&quot;: &quot;howdy {{&lt;uuid of trigger&gt;==user}}!&quot;<br>  }<br>}</pre><p>This sends a message to the person who triggered the workflow. Note that the user object can either have a ref member that refers to a value from a previous step or a “value” member that’s a literal value. The message can also make reference to the output from the trigger and previous steps using that mustache-ish syntax.</p><h3>Let’s break it</h3><p>You can craft your own json and upload new workflows. It’s pretty easy to sneak stuff past the upload step that violates the rules.</p><p>If the various steps don’t have actually unique ids, the editor gets sad without being overly explicit about what’s making it sad:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WM5BFuGziyYx-su1DxTVeA.png" /></figure><p>You can make poorly constructed forms with tons and tons of fields, non-unique ids, etc. The editor doesn’t complain, but when you trigger the action, the form just isn’t shown — there’s probably an entry in a lot somewhere, but there’s nothing visible to the user or the workflow author.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AzDkvaI6M4ZkJ8hrs3adrA.png" /><figcaption>Silently not shown.</figcaption></figure><p>Finally — my personal favorite — the visual builder enforces that the number of steps in a workflow be bounded by propriety and good taste. Not so when importing workflows. I wrote a <a href="https://gist.github.com/ahl/20cb5183098fb8d3231b87a13b77af39">little python script</a> that generates a workflow that sends 500 messages. This is particularly cruel because 1. it forever (about 20 minutes; I don’t know if workflows just run slow or are rate limited) and 2. it appears to be unkillable even if you unpublish the workflow (deleting it does seem to work). It would be easy to write something even more noxious… fortunately workflows do identify the author.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e66ea922fde28eabdcab21bafa170d05/href">https://medium.com/media/e66ea922fde28eabdcab21bafa170d05/href</a></iframe><p>Starting a workflow instantiates a state machine that walks through each step. It locks in the version of the workflow at the time it was initiated: changes to the published workflow don’t effect instances already in flight. This is a sensible approach and curtailed my plans for mischief. I can’t tell what happens to workflows that are never completed — do they hang around in some database forever or does Slack clean them up after awhile? I called the (undocumented) apps.actions.run API, but didn’t want to DoS the new feature. But speaking of APIs…</p><h3>Missions Impossible</h3><p>There isn’t a documented API (that I could find) for Workflow Builder. Running Slack from the browser let me sniff out some of the calls:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/695/1*gvR4A1tivg482xxnC7ta4A.png" /></figure><p>Missions! That acquisition Slack made in July of 2018. And while it’s been rebranded to Workflow Builder, the name lives on in the API.</p><p><a href="https://slackhq.com/slack-acquires-robot-pencils-missions-to-make-it-easy-for-non-tech-teams-to-streamline-work">Slack acquires Robots &amp; Pencils&#39; Missions to make it easy for non-tech teams to streamline work - Several People Are Typing</a></p><p>The <a href="https://missions.ai/">Missions website</a> doesn’t have much info left on it but I was able to dig up some details from the <a href="https://web.archive.org/web/20181016190414/http://help.missions.ai/getting-started/user-guide">Internet Archive</a>. Here what Missions supported:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q9zBre-NqyLEczTqKExnwA.png" /><figcaption>Missions.ai from the Internet Archive</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9lxXbMdfEv20y4oiC3Q7fg.png" /><figcaption>Missions.ai from the Internet Archive</figcaption></figure><p>So… a LOT more functionality in Missions than in what we see in the initial launch of Workflow Builder. It probably gives a good sense of the kinds of stuff to come.</p><h3>Where Workflow Happens</h3><p>Zapier already supports vastly more functionality than Workflow Builder, but I’m sure being built into Slack is compelling for a bunch of users. Even with the extremely limited functionality, you can see how triggers and steps are built to be generalized. It’s also clear that workflows are an important piece of <a href="https://medium.com/@ahl/embracing-the-saas-unbundleverse-d0be5d0b079c">Slack’s broader platform ambitions</a>. There are going to be more triggers, more steps, and deep, broad integration with the zillions of cloud applications that Slack wants you working with through their collaboration platform.</p><p>Also <a href="https://www.cnbc.com/2019/07/11/microsoft-teams-passes-slack-in-daily-users-rajesh-jha-explains-why.html">Teams</a> doesn’t have it; <a href="https://siliconangle.com/2019/10/10/slack-claims-12-million-daily-active-users-greater-engagement-microsoft-teams/">that seems pretty important</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e9f7c3ddbe3e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Embracing the SaaS unbundleverse]]></title>
            <link>https://ahl.medium.com/embracing-the-saas-unbundleverse-d0be5d0b079c?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/d0be5d0b079c</guid>
            <category><![CDATA[slack]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[enterprise-technology]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[api-development]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Wed, 15 May 2019 20:56:21 GMT</pubDate>
            <atom:updated>2019-05-15T20:56:21.447Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DMY1uc3rDbEWRZzAPUnXAw.jpeg" /></figure><p>Previous eras of software consumption were defined by their bundles. Our current era is defined by its diverse application ecosystem, its unbundling. Even recently, corporate consumers wore their fealty to a vendor with pride: we’re a Microsoft shop, we’re an Oracle shop, we’re an IBM shop. While devoted, the relationship wasn’t monogamous. The right tool for the job, but guided by relationship as the north star. Bundling had the benefit (at least perceived) of interoperability — many tools, intermeshed, helping us work well as a team.</p><p>SaaS changed both the economics of software, and the adhesion of the bundle. Even if you wanted to be a mono-shop, it’s harder to see how to do that (or why you’d want to). Teams are picking the tools they want. Applications are able to be deeper and narrower than ever before. Consequently everyone has many more applications from a much more distributed collection of vendors.</p><p>Established and new companies are repositioning for this distributed, diverse unbundleverse. We started Transposit to help build the apps composed of the APIs from unbundled software. We’re betting on this trend. But I heard a new vision for this modern era of software a couple of weeks ago at Slack’s Frontiers conference: that there can be hubs even amid unbundling, centers in an era of decentralization. Rather than grouping like-applications, these hubs group like-activities. In an unbundled world, the monopolies will be horizontal, aggregating similar kinds of interactions across applications.</p><h3>Slack on Slack</h3><p>At the conference, Slack laid out a significant and compelling vision about becoming a major epicenter amid this sectoral unbundling. While the conference felt bold and enlightening, Slack’s summary blog post was more pedestrian: mostly new features and non-specific enthusiasm. It listed shared channels, Workflow Builder, and Gmail/Outlook gateways as some big pieces. Under the heading “Building the tools for modern work”, the video featuring CPO, Tamar Yehoshua, touches on a common theme of the conference:</p><blockquote>“But communication at work doesn’t just happen between people. We need access to data and other workflows. A typical enterprise uses more than 1000 cloud applications. These software tools fragment attention and make it harder for information to flow across an organization. So we want to do more and more to connect Slack to the critical tools behind every business.”</blockquote><h3>1071</h3><p>To anyone at Frontiers, the number 1071 will feel at least familiarly significant. I counted it in no fewer than 6 different talks by various members of the Slack ensemble. It’s the average number of cloud applications in an enterprise. (Apparently from <a href="http://go.netskope.com/rs/665-KFP-612/images/april-2017-worldwide-cloud-report.pdf">this Netskope report</a>.) This was the strong theme of the conference: companies have tons of SaaS applications and Slack is the hub for collaboration, approvals, workflows, etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/1*Q5lqxw6E-3OmFUE1bZ1mMQ.png" /><figcaption>Not sure which session; could have been any of them.</figcaption></figure><p>As an aside, Slack’s famously humble, empathetic culture was also on display. The second most common theme was speakers thanking the audience for laughing — just take your yuks and move on folks! (And <a href="https://blog.transposit.com/hypebot-is-the-hype-man-who-never-lets-me-down-cbc47d08dd66">we have the perfect hype man</a>!)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9pgjmBHTvDL_a645RxZgIA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*H0IJBcXzTlukGfOXfT6jFQ.png" /><figcaption>You’re welcome.</figcaption></figure><p>Apps form silos. Silos impede cross-team communication and collaboration. If a task needs approvals from a bunch of folks in a bunch of tools, it’s going to be faster if it happens in Slack. If people use tools infrequently, offer common tasks through Slack. And rather than gathering data from multiple apps, bring it all together in the tool everyone’s already got open, Slack. It’s the aspiring epicenter of work.</p><h3>Hub at the center of the unbundleverse</h3><p>SaaS has fractured the traditional bundles. The new monopoly opportunity is horizontal. Rather than taking over collections of applications, these hubs will own common tasks across the landscape of apps. Slack wants to own collaboration and lightweight interactions across apps. It’s a monopoly that embraces a sprawling ecosystem rather than trying to own it. These catalyze expansion and heterogeneity where bundles sought to contain it. Every major software vendor is looking for their hub opportunity.</p><p>How will Slack become this hub? How will anyone? In their S-1 (published the day after Frontiers), Slack cited several components of their growth strategy. The first 6 are different aspects of sales and marketing. The 7th addresses their platform vision (and could be copied almost verbatim from other aspiring hubs):</p><blockquote><strong>Grow our application platform and developer ecosystem</strong><br>We will continue investing to expand the number of developers building applications that integrate with Slack and to make Slack work with an increasing number of third-party and internally developed custom applications.</blockquote><p>(The 8th and final growth strategy is “Leverage artificial intelligence, machine learning and advanced search”, a shibboleth of our times I suppose.)</p><p>For any hub, there’s the core platform, the surround of apps and APIs, and a yawning gap in between. Platforms and users alike are dependent on developers to fill that divide, to write the software that wires together hub and edge. From Zapier, to Airtable, and Transposit, that’s why we’re here. As much as the hubs will define our future so will the developers, tools, and platforms that bind it all together.</p><p>—</p><p>Building Slack bots or other connections between hub and edge? <a href="https://www.transposit.com/?c=hub_medium_post">Try Transposit</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0be5d0b079c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Perfect products don’t need APIs (the rest do)]]></title>
            <link>https://ahl.medium.com/google-hire-has-an-api-finally-81c99b11de83?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/81c99b11de83</guid>
            <category><![CDATA[slack]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[integration]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Fri, 12 Apr 2019 15:46:00 GMT</pubDate>
            <atom:updated>2019-04-15T04:43:18.990Z</atom:updated>
            <content:encoded><![CDATA[<p>We had just convinced our Seed round investors that Transposit was a big idea. A big idea needs a team to build it, so when the first round closed we started hiring that team. We’d talk to candidates and write down our feedback to share with the team. At first we used Google Docs. The price was right (free), but we quickly outgrew it: we needed more structure and access control. My experience with application tracking systems (ATS) is that they’re all infuriating in their own way, imposing their structure on your process rather than adapting to it. So if we weren’t going to get what we wanted, at least we didn’t want to pay a lot for it.</p><p>Enter Google Hire.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/708/0*jYqchS6RTEicPQEa.jpg" /><figcaption>Hell is other people… trapping your data with no API</figcaption></figure><p>As anticipated, Google Hire wasn’t everything what we wanted. What it was though was cheap. It’s not terrible; it just doesn’t do anything quite the way we’d like. Filter by recruiter, handoff candidate ownership, find folks we haven’t pinged in awhile — Hire didn’t do any of those things.</p><p>No problem, right? That’s what APIs are for. <strong>Your service might not suit my needs, but an API lets me work around the parts I hate and build the extensions I need.</strong> Even if the product improved to meet our needs, there would always be a next need. But — amazingly for a Google product[1] — there was no API.</p><h3>All product need APIs</h3><p>No product is perfect for all users. APIs are the escape hatch. They’re the panacea for any complaint. They’re the answer for the long tail of features you’ll never ever get to.</p><p>With no API for Google Hire, we just suffered. And in the meantime we hired our team and <a href="https://www.transposit.com/">built our product</a> that — with some irony — made it easy to work with APIs. By a happy coincidence, Google Hire released their <a href="https://developers.google.com/hire/tenants/guides/getting-started">beta API</a> right around the time Transposit opened our beta. Transposit puts a SQL and JS interface in front of APIs, manages auth, and makes it easy to build API-driven APIs. Here are the first couple of queries I ran against the Google Hire API. (You can <a href="https://console.transposit.com/t/ahl/hire">try everything out live</a> once Google gives you API access.)</p><p>I can list all the candidates we’re tracking:</p><pre>SELECT * FROM google_hire.list_candidates<br>  WHERE tenant=‘my_tenant’</pre><p>I can see the past employers of all the folks we’re talking to:</p><pre>SELECT employmentInfo.employer FROM google_hire.list_candidates<br>  WHERE tenant=’my_tenant’ EXPAND BY employmentInfo</pre><p>(Amusingly in the context of this post, the top hit is Google)</p><h3>Solving problems</h3><p>After being held at arm’s length for far too long, I had a lot of fun exploring the newly public(-ish[2]) API. The first real problem I wanted to solve was to show the candidates whom we haven’t pinged recently. Why is this not built in? I have no idea. It was easy enough to get to with Transposit. We treat each API like a view or table within a database; getting the data I wanted amounted to a JOIN between the candidates and applications tables:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0103d33ee62c48d82b569ab9af8c2747/href">https://medium.com/media/0103d33ee62c48d82b569ab9af8c2747/href</a></iframe><pre>[<br>  {<br>    &quot;name&quot;: &quot;tenants/abc/candidates/def&quot;,<br>    &quot;job&quot;: &quot;tenants/acb/jobs/ghi&quot;,<br>    &quot;candidate&quot;: &quot;tenants/abc/candidates/jkl&quot;,<br>    &quot;createTime&quot;: &quot;2019-04-02T00:31:48.630Z&quot;,<br>    &quot;status&quot;: {<br>      &quot;state&quot;: &quot;ACTIVE&quot;,<br>      &quot;processStage&quot;: {<br>        &quot;stage&quot;: &quot;Initial Screen&quot;,<br>        &quot;reportingCategory&quot;: &quot;SCREEN&quot;<br>      },<br>      &quot;updateTime&quot;: &quot;2019-04-03T05:27:29.321Z&quot;<br>    },<br>    &quot;personName&quot;: {<br>      &quot;givenName&quot;: &quot;David&quot;,<br>      &quot;familyName&quot;: &quot;Lightman&quot;<br>    },<br>...</pre><p>I can focus that in on a particular job by adding another SQL filter:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f3bac392e9c743010c9b25872a34158/href">https://medium.com/media/1f3bac392e9c743010c9b25872a34158/href</a></iframe><p>… and then get the data I want by plucking out particular fields and adding an ORDER BY:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1b85f16bf4ff02fa2f40b489465d7810/href">https://medium.com/media/1b85f16bf4ff02fa2f40b489465d7810/href</a></iframe><pre>[<br>  {<br>    &quot;updateTime&quot;: &quot;2019-03-12T17:25:33.176Z&quot;,<br>    &quot;givenName&quot;: &quot;Michael&quot;,<br>    &quot;familyName&quot;: &quot;Jennings&quot;<br>  },<br>  {<br>    &quot;updateTime&quot;: &quot;2019-03-21T20:33:08.454Z&quot;,<br>    &quot;givenName&quot;: &quot;Kate&quot;,<br>    &quot;familyName&quot;: &quot;Libby&quot;<br>  },<br>...</pre><p>Finally! The data I’ve wanted since we first started using Hire!</p><h3>All Hail Our Benevolent Slack Overlords</h3><p>Slack is rapidly becoming the central interface across so many tools we use, so I wanted the data from Google Hire connected up with Slack. Building a Slack slash command is a snap (<a href="https://docs.transposit.com/get-started/quickstart">check out the Transposit Quickstart</a>). You just need to point Slack at a webhook that returns the data you want:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5aed6a0835a234647aaba99de0820df2/href">https://medium.com/media/5aed6a0835a234647aaba99de0820df2/href</a></iframe><p>Now when I worry if we’ve dropped the ball on a candidate and they haven’t heard from us in a while, I can just type my Slack slash command:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/656/1*ql4TpGbGHUYKdgE_pkJH5w.jpeg" /></figure><h3>Next</h3><p>As excited as I was for the Google Hire API, the endpoints in the first beta are pretty basic. It will be extremely powerful when they open up the ability to do things like create new candidates, transition candidates between stages of the hiring process, and add comments (the mechanism we use to hand off candidates between members of our team).</p><p>An API is a critical feature of any tool we use; <strong>I’ll never again make the mistake of choosing a tool that doesn’t give me access to my data via an API</strong>. Transposit let me explore the Google Hire API interactively, check out the data, and poke around at different endpoints. You can <a href="http://www.transposit.com/">try it out</a> with the Hire API or any of the other APIs it connects to. And if this sounds like something you’d like to get involved in, <a href="https://www.transposit.com/jobs/">we’re hiring</a> (and we’re good at follow up).</p><pre>SELECT title FROM google_hire.list_jobs<br> WHERE tenant=’my_tenant’<br> AND filter=’state=OPEN’</pre><pre>[<br>  {<br>    &quot;title&quot;: &quot;Developer Advocate&quot;<br>  },<br>  {<br>    &quot;title&quot;: &quot;Growth Marketing Manager&quot;<br>  },<br>  {<br>    &quot;title&quot;: &quot;Product Marketing Manager&quot;<br>  },<br>  {<br>    &quot;title&quot;: &quot;Senior Site Reliability Engineer&quot;<br>  },<br>  {<br>    &quot;title&quot;: &quot;Software Engineer&quot;<br>  }<br>]</pre><p>[1] I call it a Google Product — and I knew this beforehand — but <a href="https://venturebeat.com/2017/04/13/google-hire-is-a-job-site-that-diane-greenes-bebop-has-been-quietly-working-on/">Hire came from the Bebop acquisition that brought Diane Greene into Google</a>. I suspect acquiring an amazing leader at the head of GCP may have been the primary goal.<br>[2] You need to <a href="https://developers.google.com/hire/tenants/guides/getting-started">sign up and get approved for the Google Hire API</a>. I wrote a pleading, but firm mail to our sales rep to nudge things along.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81c99b11de83" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Second-Hand Misogyny]]></title>
            <link>https://medium.com/swlh/second-hand-misogyny-a1d254350e86?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/a1d254350e86</guid>
            <category><![CDATA[women-in-tech]]></category>
            <category><![CDATA[founders]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Tue, 19 Sep 2017 03:56:04 GMT</pubDate>
            <atom:updated>2017-09-19T03:56:04.849Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CBXg_maREJiDcgH9lAsEZg.png" /></figure><p>About a year ago, <a href="https://twitter.com/kmonkeyjam?lang=en">my co-founder</a> and I started work on Transposit. It’s not that weird to start a company, but one way in which we were an outlier is that my co-founder is a woman. Starting a company comes with many many lessons; some of the most surprising have come from sitting next to Tina: a front-row seat of what it’s like to be a woman in tech. I see these moments of profound obliviousness and lack of empathy and squirm. I apologize to the women reading this: I know everything that follows will be obvious (but hopefully not redundant). I hope for the men reading this that it will help them be more aware of the subtle (and glaring) ways tech is unwelcoming / discriminatory / hostile to women.</p><h3>Disclaimers</h3><p>This is not meant to be the breathless allybrag in vogue on Medium / Twitter / HN / LinkedIn. Greater awareness has, I think, made me better, but I have been flawed and unaware and (despite my best efforts) will be again at times. I’ve definitely fucked up, not realized, and not apologized. My hope is that others can learn from lessons that would have been instructive to me.</p><p>Others have and will write more authoritatively and more eloquently on the subject. Go read about the need for<a href="https://medium.com/@zephoria/tech-culture-can-change-ad1ac3e795d5"> recognition, repentance, respect, and reparation</a> from my friend, danah. Read this distressing story about two folks in a service role<a href="https://twitter.com/i/moments/839950218099576832"> swapping genders</a> and how massively their experiences improved/declined; I lamented how oblivious I must have been when running a technical support organization. And if you’d just like to cringe yourself into a migraine, either of these will do the trick: <a href="https://www.recode.net/2017/6/13/15795612/uber-board-member-david-bonderman-women-talk-too-much-sexism">Yes, Uber board member David Bonderman said women talk too much at an all-hands meeting about sexism at Uber</a>, <a href="https://where.coraline.codes/blog/my-year-at-github/">Antisocial Coding: My Year At GitHub</a>.</p><h3>When you assume…</h3><p>Tina and I attend a lot of meetings together. We meet prospective investors, advisors, partners, customers, colleagues (engineers!). We’re small and lean; we do our own scheduling. Often the folks we’re meeting with are big (and fancy); when it comes time to set up a meeting they have people for that. I can’t even count the number of times that scheduling emails drop me and go to Tina. Or I get invited to the scheduled meeting, but Tina doesn’t. The perpetrators of this are just as often women as men. I won’t claim I don’t know why they make this assumption, but it’s vexing that they continue to act on it.</p><p>When I was a kid I used to read Dave Barry’s column in a PRINT NEWSPAPER (millennials, if you made it this far, I’ll wait while you show yourselves the door); I think of this quote when people carelessly create these awkward moments:</p><blockquote>You should never say anything to a woman that even remotely suggests you think she’s pregnant unless you can see an actual baby emerging from her at that moment.</blockquote><p>I recently asked someone if they’d extend an invitation to my Chief Technology Officer, Tina; fine, he responded, as long as she’s technical. Each individual time a woman is presumed to be less technical or have a particular role might seem like not a big deal, forgivable, but think about what an asshole you look like when you’re the 1,000th person to do it. Think about Dave Barry’s advice.</p><h3>Knock knock. Who’s there?</h3><p>This was definitely a surprise: to some people Tina is, apparently, invisible. If I hadn’t witnessed it personally I might have found it hard to believe. We’ve sat through so many meetings where the person we were talking to never looked at her. This includes meetings with prospective customers (whose money we want) and prospective advisors (who, presumably, want our money). Not only is it demoralizing, but it makes it hard to participate — forget about inhaling or clearing your throat, the subtle social cues that we’d like a turn to speak. They wouldn’t see Tina if she were waving semaphores. It leads to these am-I-crazy moments where I ask, did that guy not look at you for the full 90 minutes?</p><p>Standing around at a conference I’ve seen men do this weird thing. Rather than facing inward to the group to create a multi-way conversation, they orient themselves to face me, physically closing Tina out of the conversation. My strategy is awkward overcompensation. I’ll try shifting my body to face Tina, directing questions to her, or excusing myself from the conversation briefly. It never works.</p><h3>Take my wife… please!</h3><p>I was at a tech meeting, and while you might assume without my stating it, the attendees were mostly men. In fact Tina was the only woman who wasn’t on the event staff. The presenter was discussing a use case with Pinterest as a motivating example. You might not know much about Pinterest, he explained, but ask your wife or girlfriend, ho ho ho. In moments like these I feel my brain waving a hasty adieu to my body: I know you need to stay, but I’ve got to get out of here. Why not use an example that you think will resonate with your audience? Or why not just explain the aspects of Pinterest that are relevant to the example? Less obviously misogynistic uses of the wife/girlfriend prop still serve to exclude and divide. I’ve been guilty of looking for that cheap laugh: me, ostensibly, hapless and my wife domineeringly keeping me in line. We feel like exclusion can create this greater notion of cohesion or rapport; it should be obvious, but we forget that it’s at the expense of the people we’re excluding (even if they aren’t in the room).</p><h3>The devil you know</h3><p>Casual sexism from strangers is a constant grind for women in tech. It’s even more toxic when it comes from close collaborators, people who shouldn’t just <strong><em>assume</em></strong> better, but should <strong><em>know</em></strong> better. Men, if your female peer gets credit that you rightly deserve — either in full or by proportion — take some solace that it’s the statistical equivalent of tripping over a SHA-1 collision. Examples of women being excluded or diminished happen every day in subtle and glaring ways; I continue to try to be more aware.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a1d254350e86" width="1" height="1" alt=""><hr><p><a href="https://medium.com/swlh/second-hand-misogyny-a1d254350e86">Second-Hand Misogyny</a> was originally published in <a href="https://medium.com/swlh">The Startup</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Starting Transposit]]></title>
            <link>https://ahl.medium.com/starting-transposit-c8cbec41a94?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8cbec41a94</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Tue, 16 May 2017 04:46:08 GMT</pubDate>
            <atom:updated>2017-06-12T21:59:20.649Z</atom:updated>
            <content:encoded><![CDATA[<p>We’ve known for a while that <a href="http://a16z.com/2016/08/20/why-software-is-eating-the-world/">software is eating the world</a>. The most vacuous thing you can tell someone about your company is that it’s a software company — every company is a software company. There are lots of ways that companies differentiate, but one of those ways is almost always in their applications. More developers are connecting more applications to more data for more users than ever before. We started Transposit for those developers.</p><h4>Building Modern Applications</h4><p>A little backstory…</p><p>As CTO at Delphix, I spent a lot of time working with customers who wanted to build applications more effectively. A key precept of DevOps is to have production-like environments early in the development process: it’s critical for rapid iteration. Delphix brings this to applications built on large relational databases. Over time I started to see more and more applications where Oracle or MS SQL was just one data connection among many. As datasets become specialized and fragmented, applications need more connections to do the same job. Richer datasets create the desire for richer applications, leading to still more connections. Data, connections, and features all add to the cost and complexity of building applications. Test data is important, but, as developers, there is a growing inefficiency in how we build applications.</p><p>My co-founder, Tina Huang, has been building applications for her whole career. From application frameworks at Apple, to Google News and Twitter, she’s been building applications that drive those modern, scale businesses. In her spare time she’d build applications for friends: connecting billing to Google calendar for a dog walker, workflow tools for local businesses, and, most recently, writing the initial backend for acclaimed startup <a href="https://winnie.com/">Winnie</a> (like Yelp for parents). I met Tina at <a href="http://shv.com/">Sutter Hill Ventures</a> where she educated me about some of the ways that writing these applications are a huge pain in the ass. From the basics like connection, authentication, and error handling to later issues like caching, pagination, and scale, there’s a lot more effort expended dealing with data than there is innovating, expressing the core idea in code.</p><h4>A Common Goal</h4><p><a href="http://dtrace.org/blogs/ahl/2016/05/13/shv/">As EIRs at Sutter Hill</a>, Tina and I started working with CIOs to understand their urgent problems. For what needs in the market could we develop new technical solutions? Naturally each CIO had a different perspective, but we started hearing some common themes:</p><blockquote>“Enterprise data is a mess.”</blockquote><blockquote>“Our applications are constantly delayed.”</blockquote><blockquote>“When we upgrade SAP, applications eventually break in subtle ways.”</blockquote><p>As the ideas for Transposit were starting to converge, one CIO just nailed it for us:</p><blockquote>“Agile and DevOps are great, but the software lifecycle slows way down once you involve data.”</blockquote><p>This so precisely encapsulated what we had seen in our previous roles and heard from these other CIOs. Tina lived that development lifecycle firsthand, seeing the complexities of building data-fueled applications. I saw it with customers as they improved a critical piece of the lifecycle, but discovered new and growing obstacles. Both Tina and I love <a href="https://en.wikipedia.org/wiki/DTrace">building for developers</a>, and we love solving critical problems. Shortly after that last CIO conversation we started building Transposit.</p><h4>Transposit</h4><p>Our mission is to make applications simpler to build. Interesting applications require data. More data from more sources — spanning old/new, on-prem/cloud, etc — involves more overhead. Transposit seeks to eliminate the drudgery of working with data, to let developers focus on their core intent. Developers hate writing one-off solutions to the litany of issues that surround these data interactions. Solving these in a framework, building durable components is what fires us up. The full software lifecycle involves testing, deployment, monitoring, debugging, upgrade, and migration. Our goal is to create better facilities for all of these than would be economical for most development teams to build themselves.</p><p>Interested in joining us on this journey? The product is in limited beta, and we’re signing up development teams who are looking for a better way to build their applications. We’re a team of five so far, and hiring more developers. If you’re a potential early adopter, team-member, or just someone who wants to find out more, we’d love to hear from you: info@transposit.com</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8cbec41a94" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Prometheus to Sisyphus]]></title>
            <link>https://ahl.medium.com/from-prometheus-to-sisyphus-8bcf807e852f?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/8bcf807e852f</guid>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[apf]]></category>
            <category><![CDATA[ars-technica]]></category>
            <category><![CDATA[dtrace]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Sun, 12 Feb 2017 17:18:37 GMT</pubDate>
            <atom:updated>2017-02-14T20:31:43.700Z</atom:updated>
            <content:encoded><![CDATA[<p>When Apple announced their new file system, APFS, in June, I hustled to be in the front row of the WWDC presentation, questions with the presenters, and then the open Q&amp;A session. I took a week to write up my notes which turned into as 12 page behemoth of a blog post — longer than my college thesis. Despite <a href="https://twitter.com/ahl/status/743258869523087361">reassurances from the tweeps</a>, I was sure that the blog post was an order of magnitude longer than the modern attention span. I was wrong; so wrong that Ars Technica wanted to republish the blog post. <a href="https://developers.slashdot.org/story/08/01/22/2156244/apple-crippled-its-dtrace-port">Never underestimate the interest in all things Apple</a>.</p><p><a href="https://arstechnica.com/apple/2016/06/a-zfs-developers-analysis-of-the-good-and-bad-in-apples-new-apfs-file-system/">A ZFS developer&#39;s analysis of the good and bad in Apple&#39;s new APFS file system</a></p><p>In that piece I left one big thread dangling. Apple shipped APFS as a technology preview, but they left out access to one of the biggest new features: snapshots. Digging around I noticed that there was a curiously named new system call, “fs_snapshot”, but explicitly didn’t investigate: the post was already too long (I thought), I had spent enough time on it, and someone else (surely! surely?) would want to pull on that thread.</p><h4>Slow News Day</h4><p>Every so often I’d poke around for APFS news, but there was very little new. Last month folks discovered that <a href="https://arstechnica.com/apple/2017/01/ios-10-3-will-be-apples-first-update-to-convert-storage-to-apfs/">APFS was coming to iOS</a> sooner rather than later. But there wasn’t anything new to play with or any revelations on how APFS would work.</p><p>I would search for “APFS snapshots”, “fs_snapshot”, anything I could think of to see if anyone had figured out how to make snapshots work on APFS. Nothing.</p><p>A few weekends ago, I decided to yank on that thread myself.</p><h4>Prometheus</h4><p>I started from the system call, wandered through Apple’s open source kernel, leaned heavily on DTrace, and eventually figured it out. Apple had shipped snapshots in APFS, they just hadn’t made it easy to get there. The folks at Ars were excited for a follow-up, and my investigation turned into this: <a href="https://arstechnica.com/apple/2017/02/testing-out-snapshots-in-apples-next-generation-apfs-file-system/">“Testing out snapshots in Apple’s next-generation APFS file system.”</a></p><p>Snapshots were there; the APIs were laid bare; I was going to bring fire to all the Mac fans; <a href="https://arstechnica.com/apple/2015/04/after-fifteen-years-ars-says-goodbye-to-john-siracusas-os-x-reviews/">John Siracusa</a> and <a href="http://www.macworld.com/author/Andy-Ihnatko/">Andy Ihnatko</a> would carry me on their shoulders down the streets of the Internet.</p><h4>Sisyphus</h4><p>On the eve that this new piece was about to run I was nervously scrolling through Twitter as I took the bus home from <a href="http://www.transposit.com/">work</a>. Now that I had invested the time to research and write-up APFS snapshots I didn’t want someone else beating me to the punch.</p><p>Then I found this and my heart dropped:</p><h3>Rich Trouton on Twitter</h3><p>Slides from the &quot;Storing our digital lives: Mac filesystems from MFS to APFS&quot; session at MacTech Conference 2016: https://t.co/uJJuqLL8n9</p><p>Skim past the craziness of MFS and the hairball of HFS, and start digging through the APFS section. Slide 49, “APFS Snapshots” and there it is “apfs_snapshot” — not a tool that anyone laboriously reverse engineered, deciphering system calls and semi-published APIs — a tool shipped from Apple and included in macOS by default. F — .</p><p>Apple had secreted this utility away (along with some others) in /System/Library/Filesystems/apfs.fs/Contents/Resources/</p><h4>What To Do?</h4><p>The article that was initially about a glorious act of discovery had become an article about the reinvention of the wheel. Conversely, vanishingly few people would recognize this as rediscovery since the apfs_snapshot tool was so obscure (9 hits on Google!).</p><p>We toned down the already modest chest-thumping and published the article this morning to a pretty nice response so far. I might have happier as a FAKE NEWS Prometheus, blissfully unaware of the pre-existence of fire, but I would have been mortified when the inevitable commenter, one of the few who had used apfs_snapshot, crushed me with my own boulder.</p><p><a href="https://arstechnica.com/apple/2017/02/testing-out-snapshots-in-apples-next-generation-apfs-file-system">Testing out snapshots in Apple&#39;s next-generation APFS file system</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8bcf807e852f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DTrace at Home]]></title>
            <link>https://ahl.medium.com/dtrace-at-home-145ba773371e?source=rss-236bcfb1163e------2</link>
            <guid isPermaLink="false">https://medium.com/p/145ba773371e</guid>
            <category><![CDATA[lldb]]></category>
            <category><![CDATA[adobe-illustrator]]></category>
            <category><![CDATA[dtrace]]></category>
            <dc:creator><![CDATA[Adam Leventhal]]></dc:creator>
            <pubDate>Sun, 18 Dec 2016 04:19:59 GMT</pubDate>
            <atom:updated>2017-09-26T22:06:52.516Z</atom:updated>
            <content:encoded><![CDATA[<p>I had been procrastinating making the family holiday card. It was a combination of having a lot on my plate and dreading the formulation of our annual note recapping the year; there were some great moments, but I’m glad I don’t have to do 2016 again. It was just before midnight and either I’d make the card that night or leave an empty space on our friends’ refrigerators. Adobe Illustrator had other ideas:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/842/1*75QP1iSxzMfJGmoUJ9ZJgg.png" /><figcaption>Unable to set maximum number of files to be opened.</figcaption></figure><p>I’m not the first person to hit this. The problem seems to have existed since CS6 was released in 2012. None of the solutions was working for me, and — inspired by Sara Mauskopf’s <a href="https://medium.com/startup-grind/how-to-start-a-company-with-no-free-time-b70fbe7b918a#.uujdblxc6">excellent post</a> — I was rapidly running out of the time bounds for the project. Enough; I’d just DTrace it.</p><p>A colleague scoffed the other day, “I mean, how often do you actually use DTrace?” In his mind DTrace was for big systems, critical system, when dollars and lives were at stake. My reply: I use DTrace every day. I can’t imagine developing software without DTrace, and I use it when my laptop (not infrequently) does something inexplicable (I’m forever grateful to the <a href="http://dtrace.org/blogs/ahl/2006/08/07/dtrace_on_mac_os_x/">Apple team</a> that ported it to Mac OS X).</p><p>First I wanted to make sure I had the name of the Illustrator process right:</p><pre>$ sudo dtrace -n ‘syscall:::entry{ @[execname] = count(); }’<br>dtrace: description ‘syscall:::entry’ matched 500 probes<br>^C<br>pboard 1<br>watchdogd 2<br>awdd 3<br>...<br>com.apple.WebKit 7065<br>Google Chrome He 7128<br>Google Chrome 8099<br>Adobe Illustrato 36674</pre><p>Glad I checked: “Adobe Illustrato”. Now we can be pretty sure that Illustrator is failing on setrlimit(2) and blowing up as result. Let’s confirm that it is in fact returning -1:</p><pre>$ sudo dtrace -n &#39;syscall::setrlimit:return/execname == &quot;Adobe Illustrato&quot;/{ printf(&quot;%d %d&quot;, arg1, errno); }&#39;</pre><pre>dtrace: description &#39;syscall::setrlimit:return&#39; matched 1 probe<br>CPU     ID                    FUNCTION:NAME<br>  0    532                 setrlimit:return -1 1</pre><p>There it is. And setrlimit(2) is failing with errno 1 which is EPERM (value too high for non-root user). I already tuned up the files limit pretty high. Let’s confirm that it is in fact setting the files limit and check the value to which it’s being set. To write this script I looked at the documentation for setrlimit(2) (<a href="https://truss.works/blog/2016/12/9/man-splained">hooray for man pages!</a>) to determine that the position of the resource parameter (arg0) and the type of the value parameter (struct rlimit). I needed the DTrace copyin() subroutine to grab the structure from the process’s address space:</p><pre>$ sudo dtrace -n &#39;syscall::setrlimit:entry/execname == &quot;Adobe Illustrato&quot;/{ this-&gt;r = *(struct rlimit *)copyin(arg1, sizeof (struct rlimit)); printf(&quot;%x %x %x&quot;, arg0, this-&gt;r.rlim_cur, this-&gt;r.rlim_max);  }&#39;</pre><pre>dtrace: description &#39;syscall::setrlimit:entry&#39; matched 1 probe<br>CPU     ID                FUNCTION:NAME<br>  0    531              setrlimit:entry 1008 2800 7fffffffffffffff</pre><p>Looking through /usr/include/sys/resource.h we can see that 1008 corresponds to the number of files (RLIMIT_NOFILE | _RLIMIT_POSIX_FLAG). Illustrator is trying to set that value to 0x7fffffffffffffff or 2⁶³-1. Apparently too big; I filed any latent curiosity for another day.</p><p>The quickest solution was to use DTrace again to whack a smaller number into that struct rlimit. Easy:</p><pre>$ sudo dtrace -w -n &#39;syscall::setrlimit:entry/execname == &quot;Adobe Illustrato&quot;/{ this-&gt;i = (rlim_t *)alloca(sizeof (rlim_t)); *this-&gt;i = 10000; copyout(this-&gt;i, arg1 + sizeof (rlim_t), sizeof (rlim_t)); }&#39;</pre><pre>dtrace: description &#39;syscall::setrlimit:entry&#39; matched 1 probe<br>dtrace: could not enable tracing: Permission denied</pre><p>Oh right. Thank you <a href="https://en.wikipedia.org/wiki/System_Integrity_Protection">SIP</a>. This is a new laptop (at least a new motherboard due to some bizarre issue) which probably contributed to Illustrator not working when once it did. Because it’s new I haven’t yet disabled the part of SIP that prevents you from using DTrace on the kernel or in destructive mode (e.g. copyout()). <a href="http://internals.exposed/blog/dtrace-vs-sip.html">It’s easy enough to disable</a>, but I’m reboot-phobic — I hate having to restart my terminals — so I went to plan B: lldb.</p><p>First I used DTrace to find the code that was calling setrlimit(2): using some knowledge of the x86 ISA/ABI:</p><pre>$ sudo dtrace -n &#39;syscall::setrlimit:return/execname == &quot;Adobe Illustrato&quot; &amp;&amp; arg1 == -1/{ printf(&quot;%x&quot;, *(uintptr_t *)copyin(uregs[R_RSP], sizeof (uintptr_t)) - 5) }&#39;</pre><pre>dtrace: description &#39;syscall::setrlimit:return&#39; matched 1 probe<br>CPU     ID                    FUNCTION:NAME<br>  0    532                 setrlimit:return 1006e5b72<br>  0    532                 setrlimit:return 1006e5b72</pre><p>I ran it a few times to confirm the address of the call instruction and to make sure the location wasn’t <a href="https://en.wikipedia.org/wiki/Address_space_layout_randomization#OS_X">being randomized</a>. If I wasn’t in a rush I might have patched the binary, but Apple’s Mach-O Object format always confuses me. Instead I used lldb to replace the call with a store of 0 to %eax (to evince a successful return value) and some nops as padding (hex values I remember due to personal deficiencies):</p><pre>(lldb) break set -n _init<br>Breakpoint 1: 47 locations.<br>(lldb) run<br>...<br>(lldb) di -s 0x1006e5b72 -c 1<br>0x1006e5b72: callq  0x1011628e0     ; symbol stub for: setrlimit<br>(lldb) memory write 0x1006e5b72 0x31 0xc0 0x90 0x90 0x90<br>(lldb) di -s 0x1006e5b72 -c 4<br>0x1006e5b72: xorl   %eax, %eax<br>0x1006e5b74: nop<br>0x1006e5b75: nop<br>0x1006e5b76: nop</pre><p>Next I just did a process detach and got on with making that holiday card…</p><h4>DTrace Every Day</h4><p>DTrace was designed for solving hard problems on critical systems, but the need to understand how systems behave exists in development and on consumer systems. Just because you didn’t write a program doesn’t mean you can’t fix it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=145ba773371e" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>