http://blog.segiddins.me/2024-03-15T15:00:00ZBlog AuthorResidency Updatehttp://blog.segiddins.me/2024/03/15/residency-update/2024-03-15T15:00:00Z2024-03-15T15:02:00+00:00Article Author<p>Welcome to my sixth update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.</p>
<h2 id="fixing-a-common-source-of-oncall-pages">Fixing a common source of ONCALL pages</h2>
<p>As I mentioned <a href="https://blog.segiddins.me/2024/03/08/residency-update/#fixing-a-common-source-of-oncall-pages">last week</a>, I had found that by far our most expensive query was for reverse dependencies of a gem.
I shipped my fix last weekend, and it sure made a difference.</p>
<p><img alt="alt text" width="1560" height="546" src="/images/2024-03-15-residency-update/reverse_dependencies_p75.png" /></p>
<h2 id="fixing-n-1-queries">Fixing N+1 Queries</h2>
<p>Unfortunately, most of my week was spent on RubyGems.org operational issues.
I had noticed that many of our slowest endpoints had a very large number of queries being executed,
and I spent a bunch of time digging into root causes in DataDog to figure out why.
It turned out we had a spate of N+1 queries, and heavy automated usage of the site’s API
(along with scraping of the HTML pages) caused heavy load on those endpoints.</p>
<p>As a result, our postgres database got overloaded several times during the week, leading to multiple
members of the oncall rotation (including yours truly) to be paged over and over.
It wasn’t fun.</p>
<p>Fortunately, the Rails ecosystem has several tools to help narrow in on N+1 queries!
After slapping <code>strict_loading</code> on several queries and adding in <code>includes</code> and <code>preload</code>
calls, I brought out the big guns to find the remaining offenders.</p>
<p>Setting up the wonderful <a href="https://rubygems.org/gems/prosopite">prosopite</a> gem led to <a href="https://github.com/rubygems/rubygems.org/pull/4525">several more fixes</a>.
After a hurried emergency deploy on Thursday afternoon, we saw an immediate improvement
across the board and the site once again appears to be stable!</p>
<p><img alt="alt text" width="1550" height="524" src="/images/2024-03-15-residency-update/image.png" /></p>
<p><img alt="alt text" width="1528" height="552" src="/images/2024-03-15-residency-update/image-1.png" /></p>
<p>Top requests over the past 24 hours</p>
<p><img alt="alt text" width="3172" height="754" src="/images/2024-03-15-residency-update/image-2.png" /></p>
<p>Average time spent per request on Api::V1::VersionsController#show</p>
<p><img alt="alt text" width="1550" height="526" src="/images/2024-03-15-residency-update/image-3.png" /></p>
<p>Problematic N+1 query (getting gem version download count)</p>
<p><img alt="alt text" width="2140" height="598" src="/images/2024-03-15-residency-update/image-4.png" /></p>
<p>Finally, it appears that there are some users who are scraping every gem & version page/endpoint.
We would strongly reccomend that researchers & other automated platforms use the data dumps we provide if possible,
and make decisions on what endpoints to call to refresh data based upon changes in the <code>/versions</code> file, which is statically generated
and thus causes no load on the rails app or database.</p>
<h2 id="sigstore">Sigstore</h2>
<p>Few updates this week, due to the aformentioned operational work.
I did make progress on protobuf compliance, though!</p>
Residency Updatehttp://blog.segiddins.me/2024/03/08/residency-update/2024-03-08T19:00:00Z2024-03-08T19:37:43+00:00Article Author<p>Welcome to my fifth update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.</p>
<p>The past two weeks I have been heads-down on a pure-ruby Sigstore implementation, which has a lot of moving parts.
I hope to outline some of the interesting challenges I’ve encountered along the way, either in this update or in another venue.</p>
<h2 id="sigstore-verification">Sigstore Verification</h2>
<p>This is my big project for the month. (At least a month, it’s a real big one.)</p>
<p>We have a working implementation of <code>verify</code> and <code>verify-bundle</code> in the <a href="https://github.com/segiddins/sigstore-cosign-verify">sigstore verifier</a> that is passing <em>most</em> of the non-signing conformance tests. I still have to implement DSSE envelope verification and CT log verification.</p>
<p>As a part of this, there is also a functional TUF client, that is in desparate need of testing.</p>
<p>I have verified that the implementation works on Ruby 3.1+ (3.0 will be supported easily as well) and TruffleRuby, but there are a <a href="https://github.com/jruby/jruby/issues/8146">pair of</a> <a href="https://github.com/jruby/jruby-openssl/issues/292">JRuby issues</a> that prevent the verifier from working there.</p>
<p>Next week, I plan to continue my valiant effort to write sufficient test coverage for the verifier, and to implement the missing verification steps.</p>
<h2 id="plain-ruby-protos">Plain Ruby Protos</h2>
<p>As I hinted last update, as a part of the sigstore work I’ve implemented a pure <a href="https://github.com/segiddins/protobug">Ruby protobuf runtime & compiler</a>.
This library is meant to be easily embeddable, enabling the sigstore verifier to vendor built Sigstore protos
and use them without needing to take a dependency on a precompiled protobuf gem.</p>
<p>I’m still in the process of fleshing out the functionality, but over half of the protobuf conformance tests are now passing!</p>
<p>I’ve spent so much time dealing with binary data in Ruby lately, I feel like there’s a good talk to be written about <code>pack</code> and <code>unpack</code> and binary strings and integer bit manipulation.</p>
<h2 id="fixing-a-common-source-of-oncall-pages">Fixing a common source of ONCALL pages</h2>
<p>The PR is still a work in progress, but I <a href="https://github.com/rubygems/rubygems.org/pull/4512">fixed the most common source of ONCALL pages</a> for the RubyGems.org team. This was a good 6 hours of debugging to even reproduce and narrow down the root cause, but it turns out that swapping the order of the two operands on an <code>ON</code> clause in a join completely changes the query plan that gets used, getting postgres to use the index on the joined table instead of doing a full table scan. It looks to be on the order of a 1500x improvement in query time, for the query that the DB was spending the most time on. Wild.</p>
Residency Updatehttp://blog.segiddins.me/2024/02/23/residency-update/2024-02-24T05:00:00Z2024-03-06T21:21:06+00:00Article Author<p>Welcome to my fourth update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.
Sorry for the lack of updates last week, I was overwhelmed with non-engineering stuff and took Friday/the weekend to do other things (like trying to set up a gaming PC).</p>
<p>This week, I wrapped up the event logging feature, and got the gem research tool to a point where it’s useful for me.
The past two weeks I shipped the gem research tool to production, and have been working on a pure Ruby implementation of sigstore verification.</p>
<h2 id="rubygems-research">rubygems-research</h2>
<p>I bit the bullet after running out of disk space multiple times, and am now renting a dedicated server from Hetzner to host the gem research tool.
I have 16 cores, 2x 3TB NVMe SSDs, 128GB of RAM, and it turns out that I was still bandwidth-constrained in ingesting all the gems.
And then I was sqlite write constrained.
Anyways, the rails app is up at <a href="https://research.rubygems.info">research.rubygems.info</a>, and the source code is up on github at <a href="https://github.com/segiddins/rubygems-research">segiddins/rubygems-research</a>. The bulk ingestion code is in the <code>go-gem-enumerate</code> directory, and as the name implies is a bunch of hacky go code.</p>
<p>I’m excited to keep hacking on this tool, and have a few ideas for features that I want to add in the future (see the GitHub issues on the repo). Feel free to chime in with more issues, and I’m hoping to explore the space of “medium data” rails apps further (yes, I submitted a talk on this subject to railsconf)! (Especially concerning using sqlite).</p>
<h2 id="sigstore-verification">Sigstore Verification</h2>
<p>I’ve been deep in the weeds implementing a pure Ruby sigstore verifier, and it’s been a rabbit hole.
I have something working and passing most of the compliance test suite, but I’m not quite ready to push the code up yet.</p>
<p>Along the way, I ended up <em>also</em> writing a pure Ruby TUF client, so I guess we got a two-for-one deal going on here.</p>
<p>A couple things to address:</p>
<ol>
<li>This all being “pure ruby” matters because the end goal is to ship sigstore support in RubyGems itself, which makes native extension dependencies a no go, and also makes relying on user-installed binaries (such as the cosign CLI) undesirable.</li>
<li>The <a href="https://github.com/sigstore/protobuf-specs">protobuf specs for the sigstore types</a> have a bunch of dependencies, so using them won’t be possible.</li>
<li>The <a href="https://github.com/sigstore/sigstore-conformance">sigstore conformance suite</a> is pretty nice, but setting it up to run locally required a rake task so I wouldn’t lose my mind (<code>sh "/Users/segiddins/Development/github.com/sigstore/sigstore-conformance/env/bin/pytest", "test", "--entrypoint=#{File.join(__dir__, "bin", "conformance-entrypoint")}", "--skip-signing", chdir: "/Users/segiddins/Development/github.com/sigstore/sigstore-conformance"</code>)</li>
<li>TUF uses a <a href="https://wiki.laptop.org/go/Canonical_JSON">non-standard JSON representation</a> for computing signatures, so I had to write a custom serializer to be able to verify signatures. I was really hoping it would at least be RFC 8785, but no such luck.</li>
<li>Rekor uses OpenAPI to define types instead of protos</li>
<li>The TUF spec is … dense</li>
</ol>
<h2 id="plain-ruby-protos">Plain Ruby Protos</h2>
<p>I might’ve started to write a pure Ruby protobuf implementation based on a new proto compiler.
I hope I don’t need to use it.</p>
Residency Updatehttp://blog.segiddins.me/2024/02/09/residency-update/2024-02-09T20:00:00Z2024-02-09T09:33:11+00:00Article Author<p>Welcome to my third update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.</p>
<p>This week, I wrapped up the event logging feature, and got the gem research tool to a point where it’s useful for me.</p>
<h2 id="event-logging">Event Logging</h2>
<p>I’m planning to roll out event logging later today! It’ll be a soft launch, as I want to make sure that the data is being collected correctly before we start advertising the feature to users.</p>
<p>I’m excited to build on this foundation and add more event types in the future.</p>
<h2 id="gem-research-tool">Gem Research Tool</h2>
<p>What a fun rabbit hole! I ran out of disk space a few times, OOM’d a 96GB RAM machine, and cursed a fair amount.
After concluding that doing all the ingestion in Ruby (and using activerecord) was too slow, I wrote a golang script to do it for me.
Then I decided to implement it in Rust, so I could easily call it from ruby, but that was slower than go.
So I went back to go, got things fast enough, and then ran out of disk space and RAM a few more times.
If I happen to be awake, <a href="https://rubygems-research.folk-dinosaur.ts.net">https://rubygems-research.folk-dinosaur.ts.net</a> should let you browse around (the app is still hosted on my local machine, as attached block storage large enough to fit the entire corpus of gems is expensive on cloud providers).
This should be pushed up to GitHub soon, and I’ll probably publish it as a gem, because why not.
The team is also excited to use this as a playground for features that we want to expose to the public eventually, like browsing gem contents and being able to query random things (like how many empty files are packed into gems: over 18 million, or how many libxml binaries are published: querying this is too slow and I’m lazy, but I have the 180 million row table to make it possible!)</p>
<h2 id="other-stuff">Other Stuff</h2>
<p>My dad has been in town this week after we went to a concert in Santa Monica last weekend, so this update is a bit short. I tried to focus on those two big things, and next week I should be able to start turning my attention to something new!</p>
Residency Updatehttp://blog.segiddins.me/2024/02/02/residency-update/2024-02-02T20:00:00Z2024-02-09T09:25:49+00:00Article Author<p>Welcome to my second (very late to be published) update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.</p>
<p>This week, I focused on addressing some inbound security reports, wrapped up feature work on event logging, and started building a new tool to assist me in researching the gems that are a part of our ecosystem.</p>
<h2 id="security-reports">Security Reports</h2>
<p>Can’t share the details here yet until the reports are disclosed, but trust me that I did work in this area.</p>
<h2 id="event-logging">Event Logging</h2>
<p>Event logging is basically feature complete! In fact, the <a href="https://github.com/rubygems/rubygems.org/pull/4406">models are already in production</a>.
You can see some screenshots linked on the <a href="https://github.com/rubygems/rubygems.org/pull/4367">massive PR</a>.
Not too much to say here outside of what’s in the PR. I’ll write up the feature on the RubyGems blog once it’s live.</p>
<h2 id="component-previews">Component Previews</h2>
<p>Now that I’ve been working on RubyGems.org for a while, I finally got around to making it slightly easier to build UI.
As a part of my event logging work, I <a href="https://github.com/rubygems/rubygems.org/pull/4407">set up a component preview system using lookbook</a>, which is a way to see what a component looks like in isolation from the rest of the app. This is a big win for me, as it makes it easier to iterate on UI changes without needing to navigate through the app to see them.
Selfishly, it’s also helpful because it’s an easy way to get code coverage on my Phlex components.</p>
<h2 id="gem-research-tool">Gem Research Tool</h2>
<p>As part of my role as a security engineer, I need to be able to quickly research the gems that are a part of our ecosystem. I started building a tool to help me do that, and it’s already been helpful in my work. It’s a rails app backed by a sqlite database that ingests data both from the <a href="https://rubygems.org/pages/data">rubygems.org data dumps</a> as well as directly from all the .gem files hosted on rubygems.org. This was a fun rabbit hole to hack on, and it already paid dividends while addressing some of the security reports that came in this week.</p>
Residency Updatehttp://blog.segiddins.me/2024/01/26/residency-update/2024-01-26T20:00:00Z2024-02-09T09:09:05+00:00Article Author<p>Welcome to my first (very late to be published) update as <a href="https://rubycentral.org/news/ruby-central-welcomes-new-software-engineer-in-residence-sponsored-by-aws/">Ruby Central’s security engineer in residence, sponsored by AWS</a>.</p>
<p>My goal is to write a short update every week, chronicling what I’ve been working on, and reminding myself that I was, in fact, productive.</p>
<p>This week, I focused on updating rubygems.org’s rack to v3 and rails defaults to 7.1, as well as kicking work on the security events log into high gear.</p>
<h2 id="rack-upgrade">Rack Upgrade</h2>
<p>That this upgrade was difficult was entirely self-inflicted. Rack 3 <a href="#">optimized parsing of form keys</a>, which broke some custom form shenanigans I had done on our admin dashboard. I spent a while trying to fix it in a more rails-y way, but <a href="#">monkey patching</a> was easiest solution.</p>
<h2 id="rails-7-1-framework-defaults">Rails 7.1 Framework Defaults</h2>
<p>We upgraded to Rails 7.0 a while back, and Rails 7.1 this past month, so it was just a bit of cleanup work to update to the newer framework defaults.
This is regular maintenance, but it’s important to keep up with the latest versions of the frameworks we use.</p>
<h2 id="soft-deleting-users">Soft-Deleting Users</h2>
<p>This has been on our wish list for a while. Not many users end up deleting their accounts, when they did, it made it hard to track what they had done. We’re now <a href="https://github.com/rubygems/rubygems.org/pull/4376">soft-deleting users</a>, which means they’re not actually deleted from the database, but are marked as deleted (and all personal information overwritten). This is largely an internal change, but it helps make the next feature far more useful.</p>
<h2 id="security-events-log">Security Events Log</h2>
<p>This is the big thing I’ve been working on. I spent a bunch of time refactoring, and think I’ve landed on a solution that isn’t too much code, and isn’t too much magic either. As things stand now, stuff is pretty well DRY’d up between events on gems & events on users, so I’m reasonably happy.</p>
<p>Along with the feature work here, I ended up implementing support for viewing them in our admin dashboard, as well as further introducing the use of components to our rails views (which I’m a big fan of).</p>
Nostalgiahttp://blog.segiddins.me/2022/10/23/nostalgia/2022-10-23T20:45:00Z2024-02-09T08:40:00+00:00Article Author<p>I’m in a window seat, at 36,000 feet, and just finished up my free mini bottle of buffalo trace. Years ago, this was my happy place. Flying somewhere, leaving some place, being treated like someone special because I spent so much damn time and dollars for the right to sit on an airplane as it took me from point A to point B.</p>
<p>Sure, I’ve been a United 1K for half a decade at this point. Racked up almost half a million millions flown in the past 6 years. But it was more than that. I was a <strong>frequent flier</strong>, damnit. I could navigate airports with my eyes closed, could tell you the best seat on every plane in UA’s fleet. Hell, I called United Airlines “UA”, because that showed how much of an insider I was.</p>
<p>Now, we’re several years and one giant global pandemic later. The world’s been turned on its head so many times over the past two and a half years, I can’t even remember if it’s currently upside-down or rightside-up. And here I am, sitting in seat 10F (have booked 10D, don’t ask questions), having consumed my in-flight freebies, and …… it’s not the same.</p>
<p>Shocker.</p>
<p>Flying was my personality. It was my schtick. “I spent a night in the Singapore airport working, and downing an entire bottle of champagne while sitting alone in the business class lounge” was not only a true statement, but a point of pride. It was how I introduced myself. It was my dating app profile. “I took a spring break in Hong Kong just to eat dumplings” was true. And I fucking loved that spring break!</p>
<p>And here I am, having flown SFO-NYC two weekends in a row for two different weddings. The travel used to be part of the fun. Enjoying the journey as much as the destination. You know the cliches. But, at least for a lot of the travel I’ve done this year, that’s been gone. Few upgrades. Chaos in airports. Ticket prices doubling verses the baselines I had memorized. Seatmates not wearing masks. Middle seats having people in them. My favorite flight times no longer on the schedule.</p>
<p>The spontaneity is gone. The absurd is gone. Chasing the sun in a lie-flat seat is… gone. The last trip I had scheduled, before the world shut down, was supposed to be vacationing in Sicily with my mom. Of course, I was going to get to Italy from San Francisco via Toronto, Mumbai, and Geneva. I loved it. Arbitrage opportunities for earning status, a silly amount of time spent in business class, just being <em>absurd</em> about flying. Saying “sign me up” doesn’t cover it – I didn’t need convincing. I sought it out. And me being me, I consistently found it.</p>
<p>And now… who knows if I can do that again. Remote work makes it harder to just disappear for a conference and say it’s all work (yes, it is all work); now I’m expected to be on Zoom and Slack constantly, even if I’m 9 time zones away and just spent 20 hours in transit. That’s not relaxing. It’s also not conducive to randomly finding Michelin star restaurants in <em>insert city here</em> to walk into for lunch. Besides, half the places I loved to do that in have been off limits until the past few weeks.</p>
<p>To top it all off, for whatever reason I have utterly failed at taking time off the past few years. Yeah, I was bad before. But now? Why take a day off to ski when I can take meetings from the slopes. Why take an extra day when going to a conference when I can be home in time to open my laptop and call into sprint planning? Seriously, why???</p>
<p>So, to tie it all into the title that I penned in my mind before opening my laptop, I think I’m nostalgic for a life I had. I’m fighting internally against the idea of giving up on being the jetsetter, the mile-chaser, the guy who flew a bunch and fucking loved it. Because I loved it. I want to find a way to recapture that love of showing up to SFO hours early for a transcon to get a free meal and drink in the lounge, or spending a weekday going out of my way to LAX or IAH to secure double-digit hours in a business class seat upgraded with PlusPoints. That shit was so fucking fun!</p>
<p>Do I know how I’m going to go back to it? No. But I’m working on it.</p>
What I Valuehttp://blog.segiddins.me/2020/12/23/what-i-value/2020-12-23T09:45:00Z2024-02-09T08:16:53+00:00Article Author<p>I’ve spent a lot of this year thinking. I think most people have.</p>
<p>I was sending some random tweets to a friend last week, and I captioned the exchange by saying “… I think this year has made me, and there’s no other way to put it, insufferably Jewish”. (Now, I know I’ve always been insufferable. I was merely commenting on the nature of the insufferability these days). He responded by first assuring me, “that’s not a bad thing” (which is how you can tell he is, indeed, one of my Jewish friends). Next, he said something that’s made me reflect a lot, that “a lot of this year has been identifying what you value”.</p>
<p>What do I value?</p>
<p>My family, certainly. A nice glass of scotch after a cold day, sure. My work, maybe (I value being <em>good</em> at what I do more than doing it, if that makes any sense).</p>
<p>Me, though? Do I value myself? Of course, if asked, I would’ve said yes. But I don’t think I truly valued myself as much as I should. And I think a big part of that was not having a <em>way</em> to value myself. A framework, if you will.</p>
<p>One of the ways I’ve found to value myself, and figure out how I fit into the world, has been embracing being a Jew. One of the morning brachot says “baruch atah hashem, she’asani yisrael” – I wake up thankful to be a Jew. But what does that mean in 2020, in San Francisco?</p>
<hr>
<p>Time for a little aside.</p>
<p>For almost 20 years, I have brushed aside the notion of prayer.
Why should I pray, when I don’t really believe that there’s an audience for those prayers?
Cue something like a decade of cynicism.</p>
<p>I intentionally stopped attending Hebrew School immediately after becoming bar mitzvah.
I was suspicious of the number of in-groups the synagogue had.
I heard so many Jewish friends deride anyone who became “more observant”.
I thought every form of observance was an obstacle to living a “normal” life.</p>
<p>Fast forward to 2016.</p>
<p>I started traveling, an in particular, that year I went to Berlin, Amsterdam, Prague, and Budapest.
All cities that used to have robust & beautiful Jewish populations.
Cities where the Jews were wiped out in the Shoah.
Where the only remains of Jewish life are synagogues turned into museums, devoid of their congregants.</p>
<p>Those trips stirred something inside me.
Tears, certainly, but the haunting beauty of the old synagogues stayed with me.
I returned back home to Chicago, attended High Holy Day services at the local shul, and that was it.</p>
<hr>
<p>In the lead up to this year’s days of awe, I spent some time listening to some talks put out by the synagogue I’d attended the year prior. (I jotted down some ramblings at the time, both <a href="https://gist.github.com/segiddins/534c029d11d27774ae2e9851513d6cf4">prior to Rosh Hashanah</a> and <a href="https://gist.github.com/segiddins/e14f9d18eab42ad463c6e6b3a659379c">right before Yom Kippur</a>.)
One week, the focus was on the Unetanneh Tokef. Particularly the conclusion.
U t’shuva, u tefillah, u tzedakah ma’avirin et-roa hagzerah.
But t’shuva, tefillah, and tzedakah can transform (or temper) the harshness of the decree.</p>
<p>I’ll skip the (very interesting!) debate about how exactly that’s supposed to work.
But one explanation has stuck with me, months after the close of the gates on neilah.
It is the idea that <em>the acts</em> of t’shuva, tefillah, and tzedakah change <em>us</em>, transform us.</p>
<p>And that’s the first explanation of prayer, tefillah, that has resonated with me.
Why pray, if I don’t believe it can change the world around me?
Because I know for certain that it can change me.</p>
<p>We repeat the same prayers over and over, every day, every week.
When I was younger, I found the repetition to be rote.
Now, I draw comfort from it, the same comfort you feel when wrapping youself in the hug of a tallit.
The words, the melodies, the sheer familiarity helps focus kavanah, intention.
The intention to really think about family members who are sick as I pray for healing.
The intention to be able to pray the words of my heart, even if they’re not written down in the siddur.
The intention to want to be myself, the best version of myself, the only version of myself.</p>
<p>Because there is no version of me who is not a Jew.
There is no part of me that can’t appreciate the magic of Shabbat as it rolls around, every single week.
There is no part of me that does not weep and mourne for the thousands of years of trauma our people have endured.</p>
<hr>
<p>This year, I hosted my own Seder for the first time.
I hosted my own Rosh Hashanah dinner for the first time.
(Both meals I cooked for 10. There is no other way to do a holiday meal).
I started attending Kabbalat Shabbat services weekly at one synagogue.
I called into Havdallah weekly with a <em>different</em> shul.
Attended high holy day services on my own, from my living room.
I blew my own shofar (sorry, neighbors!)
I read Jewish books, listened to Jewish music, made Jewish tweets.</p>
<p>In a year that’s seen so much turned upside-down, this is where I found comfort.
This is what my actions say I valued.
This is what I valued.
This is who I was.</p>
<hr>
<p>Why even bother writing this down?</p>
<p>Because I want to proudly & openly embrace this facet of myself.
Because I want to undo the decade of distance I forced upon myself.
Because, like prayer, the point is to transform myself, into the person I want to be.
And saying it out loud is just a part of how that works.</p>
This time, it was a compiler bughttp://blog.segiddins.me/2020/04/15/this-time-it-was-a-compiler-bug/2020-04-16T01:00:00Z2024-02-09T08:16:53+00:00Article Author<p>I write software for a living. At least, I pretend to. Most of the time, my job is building & running (& yes, occasionally fixing) software other people have written.</p>
<p>One of the aphorisms of software development is that “it’s never a compiler error”. Sure, it’s not <em>never</em> a compiler error, since compilers are written by humans and therefore are as flawed as any other piece of software. But it’s <em>never</em> a compiler error, in the sense that, when you find a bug, the probability of it being caused by a compiler error exists on a set of measure zero.</p>
<p>Well, today I hit the jackpot. This time, it really was a compiler bug.</p>
<p>Almost.</p>
<p>At work, we’re fortunate to have a bunch of tests.
I’m unfortunate in that my job is essentially to make sure we run those tests.</p>
<p>A (somewhat) common pattern we have is to have test cases initialize a test fixture at the start of each test method,
and at the end <code>nil</code> out the fixture and make sure it was deallocated. Makes sense, right? We want to make sure we’re not leaking objects over into the next test.</p>
<p>That pattern looks a little something like this:</p>
<pre class="highlight objective_c"><code><span class="k">@import</span> <span class="n">Foundation</span><span class="p">;</span>
<span class="k">@import</span> <span class="n">XCTest</span><span class="p">;</span>
<span class="k">@interface</span> <span class="nc">TestFixture</span> <span class="p">:</span> <span class="nc">NSObject</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">testFixtureForCountryCode</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">code</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">initWithCountryCode</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">code</span><span class="p">;</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">code</span><span class="p">;</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="n">BOOL</span> <span class="n">otherParam</span><span class="p">;</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">tearDown</span><span class="p">;</span>
<span class="k">@end</span>
<span class="k">@implementation</span> <span class="nc">TestFixture</span>
<span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="nf">testFixtureForCountryCode</span><span class="p">:(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="nv">code</span><span class="p">;</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithCountryCode</span><span class="p">:</span><span class="n">code</span><span class="p">];</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">initWithCountryCode</span><span class="o">:</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">code</span><span class="p">;</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">[</span><span class="n">self</span> <span class="nf">initWithCountryCode</span><span class="p">:</span><span class="n">code</span> <span class="nf">otherParam</span><span class="p">:</span><span class="nb">YES</span><span class="p">];</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span><span class="n">initWithCountryCode</span><span class="o">:</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">code</span> <span class="n">otherParam</span><span class="o">:</span><span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="n">otherParam</span><span class="p">;</span>
<span class="p">{</span>
<span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="nf">init</span><span class="p">];</span>
<span class="n">_code</span> <span class="o">=</span> <span class="n">code</span><span class="p">;</span>
<span class="n">_otherParam</span> <span class="o">=</span> <span class="n">otherParam</span><span class="p">;</span>
<span class="k">return</span> <span class="n">self</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">tearDown</span> <span class="p">{</span>
<span class="c1">// do stuff
</span><span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">dealloc</span><span class="p">;</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">self</span> <span class="nf">tearDown</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">@end</span>
<span class="k">@interface</span> <span class="nc">A</span> <span class="p">:</span> <span class="nc">XCTestCase</span>
<span class="k">@end</span>
<span class="k">@implementation</span> <span class="nc">A</span>
<span class="k">@end</span>
<span class="k">@interface</span> <span class="nc">Tests</span> <span class="p">:</span> <span class="nc">XCTestCase</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="n">TestFixture</span> <span class="o">*</span><span class="n">testFixture</span><span class="p">;</span>
<span class="k">@end</span>
<span class="k">@implementation</span> <span class="nc">Tests</span>
<span class="k">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">setUp</span><span class="p">;</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">super</span> <span class="nf">setUp</span><span class="p">];</span>
<span class="n">self</span><span class="p">.</span><span class="n">testFixture</span> <span class="o">=</span> <span class="p">[</span><span class="n">TestFixture</span> <span class="nf">testFixtureForCountryCode</span><span class="p">:</span><span class="s">@"en_US"</span><span class="p">];</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">tearDown</span><span class="p">;</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">testFixture</span> <span class="nf">tearDown</span><span class="p">];</span>
<span class="n">__weak</span> <span class="n">__typeof__</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">testFixture</span><span class="p">)</span> <span class="n">weakTestFixture</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">testFixture</span><span class="p">;</span>
<span class="n">self</span><span class="p">.</span><span class="n">testFixture</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
<span class="n">XCTAssertNil</span><span class="p">(</span><span class="n">weakTestFixture</span><span class="p">,</span> <span class="s">@"Expected thing to be nil"</span><span class="p">);</span>
<span class="p">[</span><span class="n">super</span> <span class="nf">tearDown</span><span class="p">];</span>
<span class="p">}</span>
<span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">test_empty</span> <span class="p">{}</span>
<span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">test_empty2</span> <span class="p">{}</span>
<span class="k">@end</span>
</code></pre>
<p>We’ve had tests that have been doing that for years. They’ve been passing. Until now.
When we’ve been switching to <a href="https://bazel.build/">Bazel</a>.</p>
<p>After months of painstaking work, we’ve got our apps building, and most of our tests building,
and almost most of them passing.</p>
<p>Except for some tests that use test fixtures. And assert that those test fixtures get deallocated.
And they passed in Xcode. And failed when run via Bazel.</p>
<p>Queue three days of chasing my tail, adding hundreds of random print statements to chase down these bugs.
Yesterday, I fixed a bug around using <code>+[NSHashTable weakObjectsHashTable]</code> (turns out, you <strong>really</strong> want <code>NSMapTableObjectPointerPersonality</code> instead of <code>NSPointerFunctionsObjectPersonality</code> when you’re potentially storing multiple proxy objects that compare equal and want them all to receive delegate callbacks. Anyways.)
I thought that was going to be the worst of it.</p>
<p>It, of course, wasn’t, because that bug on its own doesn’t warrant a blog post.</p>
<p>The code above was failing in Bazel. It passed when I used <code>alloc init</code> directly, instead of the factory class method to create the test fixture.
It passed when I moved the allocation and assignment into an <code>@autorelease</code> block.
It passed the smell test when I popped the binary into Hopper and started reading decompiled methods.</p>
<p>But it failed as written. And I was determined to get to the bottom of things.</p>
<p>Guessing that something was maybe up around autoreleases, I looked at the dissassembly, instead of Hopper’s (amazing) decompiler view.
I remembered, thanks to an old <a href="https://www.mikeash.com/pyblog/friday-qa-2014-05-09-when-an-autorelease-isnt.html">Mike Ash article</a> from back in the day,
that the modern ObjC runtime does some (extra) magic around autoreleasing values.</p>
<p>The (working) code ends up compiling to something like this in Xcode:</p>
<pre class="highlight objective_c"><code><span class="k">+</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="nf">testFixtureForCountryCode</span><span class="p">:(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="p">{</span>
<span class="n">var_18</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
<span class="n">objc_storeStrong</span><span class="p">(</span><span class="o">&</span><span class="n">var_18</span><span class="p">,</span> <span class="n">arg2</span><span class="p">);</span>
<span class="n">var_30</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithCountryCode</span><span class="p">:</span><span class="n">var_18</span><span class="p">];</span>
<span class="n">objc_storeStrong</span><span class="p">(</span><span class="o">&</span><span class="n">var_18</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">);</span>
<span class="n">rax</span> <span class="o">=</span> <span class="p">[</span><span class="n">var_30</span> <span class="nf">autorelease</span><span class="p">];</span>
<span class="k">return</span> <span class="n">rax</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
<p>whereas from Bazel it’s getting compiled down to:</p>
<pre class="highlight objective_c"><code><span class="cm">/* @class TestFixture */</span>
<span class="k">+</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="nf">testFixtureForCountryCode</span><span class="p">:(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="nv">arg2</span> <span class="p">{</span>
<span class="n">var_8</span> <span class="o">=</span> <span class="o">**</span><span class="n">___stack_chk_guard</span><span class="p">;</span>
<span class="n">var_10</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
<span class="n">objc_storeStrong</span><span class="p">(</span><span class="o">&</span><span class="n">var_10</span><span class="p">,</span> <span class="n">arg2</span><span class="p">);</span>
<span class="n">var_28</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithCountryCode</span><span class="p">:</span><span class="n">var_10</span><span class="p">];</span>
<span class="n">objc_storeStrong</span><span class="p">(</span><span class="o">&</span><span class="n">var_10</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">);</span>
<span class="n">var_30</span> <span class="o">=</span> <span class="p">[</span><span class="n">var_28</span> <span class="nf">autorelease</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">**</span><span class="n">___stack_chk_guard</span> <span class="o">==</span> <span class="n">var_8</span><span class="p">)</span> <span class="p">{</span>
<span class="n">rax</span> <span class="o">=</span> <span class="n">var_30</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="n">rax</span> <span class="o">=</span> <span class="n">__stack_chk_fail</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">rax</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
<p><code>___stack_chk_guard</code>? <code>__stack_chk_fail</code>? What is that stuff? Initially, I ignored them since they seemed inconsequential to the program’s control flow.</p>
<p>But, going back to that discussion of <code>objc_retainAutoreleaseReturnValue</code> and <code>objc_autoreleaseReturnValue</code>, and keeping in the back of my head that they inspected the calling code’s following instructions, I started hunting for differences in the <code>clang</code> invocations between Xcode and Bazel.</p>
<p>After a lot of sorting, I had a culprit. <code>-fstack-protector</code>. It seemed so innocent. Protecting the stack sounds good! The <a href="https://clang.llvm.org/docs/ClangCommandLineReference.html">clang command line reference</a> only says that <code>-fstack-protector</code> will <code>enable stack protectors for some functions vulnerable to stack smashing</code>. That doesn’t at all sound destructive!</p>
<p>However, due to the way the stack protector works (by adding instructions at the start and end of function), they can interfere with a call to <code>objc_autoreleaseReturnValue</code> being able to see it’s matching call to <code>objc_retainAutoreleaseReturnValue</code>, and then <code>[object autorelease]</code> will actually have to do an autorelease. Which means that the object will go into the autoreleasepool. And it won’t be deallocated until that pool drains. And <code>XCTestCase</code>‘s <code>-setUp</code> and <code>-tearDown</code> methods happen inside the same autoreleasepool.</p>
<p>Boom. Bug.</p>
<p>Objects here really <em>were</em> living longer under Bazel than they were in Xcode, since autoreleased objects were actually being autoreleased (and subsequently retained), instead of ending up skipping both the autorelease and the retain (<code>init</code> methods return already-retained objects).</p>
<p>Now that we’re all caught up and fully understand the bug (and have spent enough time saying “what the…” to an empty room to calm down), there are a couple of obvious solutions. The first is to, you know, write correct code. If we want to test that objects aren’t participating in an accidental retain cycle, we need to make sure that their creation happens inside an autoreleasepool that’s drained by the desired end of that objects lifetime. Moving the <code>self.testFixture = ...</code> initialization & assignment inside of an <code>@autoreleasepool</code> block will do just that for us.
And the second is to make our migration easier, by not passing <code>-fstack-protector</code> in the new build system, when it wasn’t passed in the old build system. (Or, in the case of bazel, to pass <code>--per_file_copt=".*\.m","@-fno-stack-protector"</code> on the command line, since there’s no way to get it to stop passing <code>-fstack-protector</code> and there’s no other way to sneak my <code>-fno-</code> flag in after Bazel’s flag gets added.)</p>
<p>What made this bug so fun (and infuriating) to investigate was that it sat at the intersection of a bunch of different moving pieces.
Our code was technically wrong (relying on performance optimizations in the runtime isn’t especially safe).
Bazel did something incredibly unexpected (passing <code>-fstack-protector</code> when I didn’t ask it to).
The Objective-C runtime has a performance optimization that does more than optimize (this is valid code under ARC, and yet it’s behavior is different from what ARC’s semantics say it should be).
And, finally, clang allows me to pass a compiler option that changes observable behavior, without documenting that it can do more than catch a small set of bugs.</p>
<p>So, they say that it’s never a compiler bug. And in this case, it might not be a compiler bug.
But it sure is close to one.</p>
A Boring Lifehttp://blog.segiddins.me/2020/01/21/a-boring-life/2020-01-21T17:00:00Z2020-01-21T17:20:08+00:00Article Author<p>I came across a tweet recently (it’s not that unusual these days). Someone was saying they wanted to get a KitchenAid (do it!) to be able to make homemade pizza dough, and said that this made them a boring adult now. It got me thinking, and then I wrote this.</p>
<p>I’ve contemplated a lot about my life recently. Maybe it’s something to do with being 25. Maybe it’s because I’ve gotten to watch several people around me undertake significant life changes. Maybe it’s because I’m human and we’re prone to doing this sort of thing all the time. I don’t know.</p>
<p>Anyways, some of that thinking has been in connection with talking. The sort of talking that happens whenever you see or call family, and you have to act out the same script over and over for the three billionth time, because of course your grandfather or uncle or mom’s childhood friend want to know how you are (or rather, they have to ask. they probably don’t actually want to know). Before I got a full-time job, this was actually a pretty fun script to follow. If I skipped enough conversations, there would be enough scheduled, structural changes in my life to fill out a thirty minute conversation and thus fullfil my duty as someone a generation or two younger than whoever was on the other end of the line.</p>
<p>New quarter in school? Talk about the new classes! Complain about the professors! Say what you didn’t learn about the quarter before that you were expecting to learn! Lot’s of personal stuff to talk about that wasn’t really about <em>me</em>, but rather was about the world around me. It was changing, and I could pass that off as the news in my life.</p>
<p>But now? Sure, I completed another sprint at work. I do that every two weeks. But I’m not sure the NDA I signed even allows me to say the goofy sprint name we put in JIRA, much less talk about tickets and how they’re germane to the business and somehow convince myself that moving a label over a few pixels or filing some new Xcode bug to was a worthy pursuit for a college-educated grandson. But even if I <em>could</em> talk about it: who cares? Seriously. My manager barely gets through ten minutes of my weekly status updates, and he gets paid to listen! My aunt definitely doesn’t have the context necessary to talk about bazel build hermeticity (sure, that’s probably a word) and how it interacts with <code>actool</code> and <code>.appiconset</code> bundles, but I swear I can spend a whole week at work fighting that.</p>
<p>I’m not complaining, of course. I get paid well for this and I largely enjoy it. But that’s 7-10 hours of the day, five days a week. Another 8-9 I spend sleeping (because I’m a boring adult now and don’t stay up until 4am watching Yu-Gi-Oh! reruns or playing smash any more). At least an hour is spend trying to transition myself from my bedroom to the rest of my apartment, and then back again (I’ve never been a morning person). I cook, and there’s only so many times you can talk about your 60th chicken soup of the year. Or the second night in a row making shameful mac n cheese because honestly hot cheese powder on carbs sounded good at the time.</p>
<p>I watch the same TV shows, sitting on the same couch with the same person (yes, it probably is time for the third re-watch of Downton Abbey, just because it means I’ll drink more tea). Date nights happen at the same set of restaurants (again, with the same person. They’d want me to clarify that, I’m pretty sure). Remembering to vacuum and fold laundry is an accomplishment (one for me and the other for them).</p>
<p>Writing all this down, it does sound kinda boring. And yet…</p>
<p>I can’t decide whether it’s a bad thing or not. Is it boring, or is it stable? I haven’t had to move apartments since graduating college. I’m saving money and don’t have debt (not my doing, really, so thank the family for that one). I get to buy the fancy Dyson vacuum on a whim and genuinely get pleasure from watching the dust behind the toilet dissappear (that was my one year vesting cliff present to myself, and I have no regrets). I went to four continents last year, and it’s become such a common occurence my mom gets upset when I forget to remind her that there’s another trip coming up (I swear I told her at least once. She admonishes me to forward her emails with my itinerary. I usually remember to do so before the first flight takes off). My partner and I went on an almost-two-week vacation to Australia because I found upgrade space on United (true story. We went via Houston for that). On that trip, we must’ve done at least four tasting menus, and didn’t worry about splurging too much. My parents complained they got more photos of the food than of me (this happens every trip, and I suppose I should be happy they still expect something different from me?) </p>
<p>I think some of the feeling of “boring” is centered around not having answers for other people when they ask what’s happening in my life. Because I know that the answer will, to them, sound like “same old, same old”.</p>
<p>But to me, the person living that boring, adult life? I get to try out new recipes at home. Continue my search for adequate pizza on the left coast. Make progress in my career (even though the “L4 means software engineer, L5 also means software engineer. No I’m not a manager. Yet” conversation is hella old by now). Spend another year proving I’m not so intolerable that someone won’t be willing to (sometimes) share a living space with me. Spend time in other countries, and come home with renewed hope that the world can be different from the one I experience day-to-day. Read a lot of books (yes, a bunch were about baseball. yes, a bunch were about medieval england. yes, a bunch were about the world between 1930 and 1950. yes, a bunch identify me as woefully weak in jewish philosophy). Listened to too many podcasts (I know they sell mattresses on the internet now. I’m still not buying, but thanks for asking, Casper). Made more and more internet friends, at least one with each passing conference.</p>
<p>In short (though I probably shouldn’t say this. I refuse to edit, and there’s still a few more miles left in this flight and I might continue to be verbose), I don’t know what I’d do differently. Is it boring? Is it adult? Maybe. Is it life? Absolutely, it’s my life. I’m reasonably happy with it, and I really can’t point at anything as being bad or wrong. Should I go to the gym more frequently? (Probably, just don’t ask me right after I’ve left it and everything is sore). Should I cut back on meat consumption because planet earth is dying? (Certainly, I have no excuses here). Should I spend more time being present with my partner instead of scrolling through the Hell Website? (Yeah, but the dumb tweets make for funny conversation a lot of the time). Should I call my grandparents more often? (I swerar I just called them mom, but no, don’t ask them about it). Should I care a bit less about finding sales on kitchen gadgets? (You can pry my AllClad and Shun out of my cold, dead hands).</p>
<p>So I’m not perfect. Not close. But it’s not like these are major life decisions I want a do-over on. They’re tweaks. Small adjustments I just haven’t made because I’m weak, I’m human. I sin, I repent, the transgressions are forgiven. But they’re not regrets. I don’t regret the steak I had with my mom on Friday, because we had a good dinner together. I don’t regret working a bit on Sunday night after my dad went to bed, because I got some satisfaction out of making a new test suite pass. I don’t regret flying to Oslo last November, because I made some awesome new friends (turns out, staying out until 3am dancing and drinking beer is still a good bonding activity. Carry on). I don’t regret starting to go to the gym, nor the fact that I only go three times a week. I don’t regret the fact that I don’t own a house (then I’d have to feel bad about being gone so often). I don’t regret watching the Bread episodes of the Great British Bake Off over and over because I love bread, and watching it with my partner (a wooden cutting board of buttered bread on my coffee table) still makes me smile.</p>
<p>(See, I told you the “in short” wouldn’t be short). I don’t really have a conclusion here. I guess I’m still trying to grapple with what an “adult life” is, what a “boring life” is, and what the hell I want <strong>my</strong> life to be. All I know (ok, I know a lot. One thing I know) is that I have no regrets right now, and as long as that remains true, I think I’m doing OK. Whatever type of life you want to describe mine as, I like it. Bring the next year on.</p>
Nagginghttp://blog.segiddins.me/2017/09/07/nagging/2017-09-07T20:30:00Z2017-09-07T20:30:14+00:00Article Author<p>There are several different trains of thought when it comes to software “nagging” users about updates.
Naturally, I want to talk a bit about how this works in the open-source developer tools world.</p>
<p>The spectrum of update nagging varies from “none” to “auto-updating”.
Homebrew, for example, auto-updates every so often whenever you run a <code>brew ...</code> command.
It eschews releases, and basically makes staying on what you percieve as a “stable” version impossible.
On the other end of the spectrum, tools like <code>openssl</code> will never tell you there’s a new version. You never get a “nag” to update, but you also never get a warning when major bugs are fixed (including those that could make your system vulnerable to attack).</p>
<p>A couple of things I work on (CocoaPods, Bundler) have decided to chart a middling course. They never auto-update, but they’ll tell you when the version that’s running is out-of-date. I think this is the best of both worlds – your development environment will never be silently changed without your consent, but you’ll also stay aware of new versions and be given an impetus to update.</p>
<p>And keeping your tools up-to-date is <strong>important</strong>. As someone who maintains lots of open source software, I can say with 100% confidence that <em>the only way to get support is to be on the latest version</em>. Fixes usually don’t get backported, and my first response to an issue is almost always to ask “is this fixed with the latest release?” As a maintainer, I can’t help someone who doesn’t update. And the only route I have to get people to stay up-to-date is to advise them that there’s an update available.</p>