Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]

New here?

Welcome! BoredReading is a fresh way to read high quality articles (updated every hour). Our goal is to curate (with your help) Michelin star quality articles (stuff that's really worth reading). We currently have articles in 0 categories from architecture, history, design, technology, and more. Grab a cup of freshly brewed coffee and start reading. This is the best way to increase your attention span, grow as a person, and get a better understanding of the world (or atleast that's why we built it).

29
"Ship it!" "We're agile now, baby. Move fast and break things!"" "We measure our engineers by the impact they have!" Somewhere along the way, in the midst of the agilification of software, or the software engineer salary gold rush, we forgot about craftsmanship. I have been in big tech, startups, consultancies, and even government. These are all different environments with one key similarity: code quality is low, especially as of late. Don't get me wrong, there are pockets of good code quality. Isolated instances of true care and craftsmanship. But, by and large, what I see now is people trying to ship products as fast as possible without regard for the maintenance burden 1, 2, 5, 10 years down the road. So, what's going on? I don't truly know, but here are my top contending theories. Perverse incentives surrounding "impact" # Big tech is all about considering "impact" when evaluating engineer performance and making promotion decisions. Unfortunately, "impact" is almost always measured...
a year ago

Improve your reading experience

Logged in users get linked directly to articles resulting in a better reading experience. Please login for free, it takes less than 1 minute.

More from pcloadletter

My articles don't belong on certain social networks

I write this blog because I enjoy writing. Some people enjoy reading what I write, which makes me feel really great! Recently, I took down a post and stopped writing for a few months because I didn't love the reaction I was getting on social media sites like Reddit and Hacker News. On these social networks, there seems to be an epidemic of "gotcha" commenters, contrarians, and know-it-alls. No matter what you post, you can be sure that folks will come with their sharpest pitchforks to try to skewer you. I'm not sure exactly what it is about those two websites in particular. I suspect it's the gamification of the comment system (more upvotes = more points = dopamine hit). Unfortunately, it seems the easiest way to win points on these sites is to tear down the original content. At any rate, I really don't enjoy bad faith Internet comments and I have a decent-enough following outside of these social networks that I don't really have to endure them. Some might argue I need thicker skin. I don't think that's really true: your experience on the Internet is what you make of it. You don't have to participate in parts of it if you don't want. Also, I know many of you reading this post (likely RSS subscribers at this point) came from Reddit or Hacker News in the first place. I don't mean to insult you or suggest by any means that everyone, or even the majority of users, on these sites are acting in bad faith. Still, I have taken a page from Tom MacWright's playbook and decided to add a bit of javascript to my website that helpfully redirects users from these two sites elsewhere: try { const bannedReferrers = [/news\.ycombinator\.com/i, /reddit\.com/i]; if (document.referrer) { const ref = new URL(document.referrer); if (bannedReferrers.some((r) => r.test(ref.host))) { window.location.href = "https://google.com/"; } } } catch (e) {} After implementing this redirect, I feel a lot more energized to write! I'm no longer worried about having to endlessly caveat my work for fear of getting bludgeoned on social media. I'm writing what I want to write and, if for those of you here to join me, I say thank you!

10 months ago 88 votes
Write code that you can understand when you get paged at 2am

The older I get, the more I dislike clever code. This is not a controversial take; it is pretty-well agreed upon that clever code is bad. But I particularly like the on-call responsiblity framing: write code that you can understand when you get paged at 2am. If you have never been lucky enough to get paged a 2am, I'll paint the picture for you: A critical part of the app is down. Your phone starts dinging on your nightstand next to you. You wake up with a start, not quite sure who you are or where you are. You put on your glasses and squint at the way-too-bright screen of your phone. It's PagerDuty. "Oh shit," you think. You pop open your laptop, open the PagerDuty web app, and read the alert. You go to your telemetry and logging systems and figure out approximate whereabouts in the codebase the issue is. You open your IDE and start sweating: "I have no idea what the hell any of this code means." The git blame shows you wrote the code 2 years ago. You thought that abstraction was pretty clever at the time, but now you're paying a price: your code is inscrutable to an exhausted, stressed version of yourself who just wants to get the app back online. Reasons for clever code # There are a few reasons for clever code that I have seen over my career. Thinking clever code is inherently good # I think at some point a lot of engineers end up in a place where they become very skilled in a language before they understand the importance of writing clean, readable code. Consider the following two javascript snippets: snippet 1 const sum = items.reduce( (acc, el) => (typeof el === "number" ? acc + el : acc), 0 ); snippet 2 let sum = 0; for (const item of items) { if (typeof item === "number") { sum = sum + item; } } At one point in my career, I would have assumed the first snippet was superior: fewer lines and uses the reduce method! But I promise far more engineers can very quickly and easily understand what's going on in the second snippet. I would much rather the second snippet in my codebase any day. Premature abstraction # Premature abstractions tend to be pretty common in object-oriented languages. This stackexchange answer made me laugh quite a bit, so I'll use it as an example. Let's say you have a system with employee information. Well perhaps you decide employees are types of humans, so we'd better have a human class, and humans are a type of mammal, so we'd better have a mammal class, and so on. All of a sudden, you might have to navigate several layers up to the animal class to see an employee's properties and methods. As the stackexchange answer succinctly put it: As a result, we ended up with code that really only needed to deal with, say, records of employees, but were carefully written to be ready if you ever hired an arachnid or maybe a crustacean. DRY dogma # Don't Repeat Yourself (DRY) is a coding philosophy where you try to minimize the amount of code repeated in your software. In theory, even repeating code once results in an increased chance that you'll miss updating the code in both places or having inconsistent behavior when you have to implement the code somewhere else. In practice, DRYing up code can sometimes be complex. Perhaps there is a little repeated code shared between client and server. Do we need to create a way to share this logic? If it's only one small instance, it simply may not be worth the complexity of sharing logic. If this is going to be a common issue in the codebase, then perhaps centralizing the logic is worth it. But importantly we can't just assume that one instance of repeated code means we must eliminate the redundancy. What should we aim for instead? # There's definitely a balance to be struck. We can't have purely dumb code with no abstractions: that ends up being pretty error prone. Imagine you're working with an API that has some set of required headers. Forcing all engineers to remember to include those headers with every API call is error-prone. file1 fetch("/api/users", { headers: { Authorization: `Bearer ${token}`, AppVersion: version, XsrfToken: xsrfToken, }, }); fetch(`/api/users/${userId}`, { headers: { Authorization: `Bearer ${token}`, AppVersion: version, XsrfToken: xsrfToken, }, }); file2 fetch("/api/transactions", { headers: { Authorization: `Bearer ${token}`, AppVersion: version, XsrfToken: xsrfToken, }, }); file3 fetch("/api/settings", { headers: { Authorization: `Bearer ${token}`, AppVersion: version, XsrfToken: xsrfToken, }, }); Furthermore, having to track down every instance of that API call to update the headers (or any other required info) could be challenging. In this instance, it makes a lot of sense to create some kind of API service that encapsulates the header logic: service function apiRequest(...args) { const [url, headers, ...rest] = args; return fetch( url, { ...headers, Authorization: `Bearer ${token}`, AppVersion: version, XsrfToken: xsrfToken, }, ...rest ); } file1 apiRequest("/api/users"); apiRequest(`/api/users/${userId}`); file2 apiRequest("/api/transactions"); file3 apiRequest("/api/settings"); The apiRequest function is a pretty helpful abstraction. It helps that it is a very minimal abstraction: just enough to prevent future engineers from making mistakes but not so much that it's confusing. These kinds of abstractions, however, can get out of hand. I have see code where making a request looks something like this: const API_PATH = "api"; const USER_PATH = "user"; const TRANSACTIONS_PATH = "transactions"; const SETTINGS_PATH = "settings"; createRequest( endpointGenerationFn, [API_PATH, USER_PATH], getHeaderOverrides("authenticated") ); createRequest( endpointGenerationFn, [API_PATH, USER_PATH, userId], getHeaderOverrides("authenticated") ); There's really no need for this. You're not saving all that much for making variables instead of using strings for paths. In fact, this ends up making it really hard for someone debugging the code to search! Typically, I'd lok for the string "api/user" in my IDE to try to find the location of the request. Would I be able to find it with this abstraction? Would I be able to find it at 2am? Furthermore, passing an endpoint-generation function that consumes the path parts seems like overkill and may be inscrutable to more junior engineers (or, again, 2am you). Keep it as simple as possible # So I think in the end my message is to keep your code as simple as possible. Don't create some abstraction that may or may not be needed eventually. Weigh the maintenance value of DRYing up parts of your codebase versus readability.

10 months ago 86 votes
The ChatGPT wrapper product boom is an uncanny valley hellscape

Here we go again: I'm so tired of crypto web3 LLMs. I'm positive there are wonderful applications for LLMs. The ChatGPT web UI seems great for summarizing information from various online sources (as long as you're willing to verify the things that you learn). But a lot fo the "AI businesses" coming out right now are just lightweight wrappers around ChatGPT. It's lazy and unhelpful. Probably the worst offenders are in the content marketing space. We didn't know how lucky we were back in the "This one weird trick for saving money" days. Now, rather than a human writing that junk, we have every article sounding like the writing voice equivalent of the dad from Cocomelon. Here's an approximate technical diagram of how these businesses work: Part 1 is what I like to call the "bilking process." Basically, you put up a flashy landing page promising content generation in exchange for a monthly subscription fee (or discounted annual fee, of course!). No more paying pesky writers! Once the husk of a company has secured the bag, part 2, the "bullshit process," kicks in. Customers provide their niches and the service happily passes queries over to the ChatGPT (or similar) API. Customers are rewarded with stinky garbage articles that sound like they're being narrated by HAL on Prozac in return. Success! I suppose we should have expected as much. With every new tech trend comes a deluge of tech investors trying to find the next great thing. And when this happens, it's a gold rush every time. I will say I'm more optimistic about "AI" (aka machine learning, aka statistics). There are going to be some pretty cool applications of this tech eventually—but your ChatGPT wrapper ain't it.

10 months ago 106 votes
Quality is a hard sell in big tech

I have noticed a trend in a handful of products I've worked on at big tech companies. I have friends at other big tech companies that have noticed a similar trend: The products are kind of crummy. Here are some experiences that I have often encountered: the UI is flakey and/or unintuitive there is a lot of cruft in the codebase that has never been cleaned up bugs that have "acceptable" workarounds that never get fixed packages/dependencies are badly out of date the developer experience is crummy (bad build times, easily breakable processes) One of the reasons I have found for these issues is that we simply aren't investing enough time to increase product quality: we have poorly or nonexistent quality metrics, invest minimally in testing infrastructure (and actually writing tests), and don't invest in improving the inner loop. But why is this? My experience has been that quality is simply a hard sell in bigh tech. Let's first talk about something that's an easy sell right now: AI everything. Why is this an easy sell? Well, Microsoft could announce they put ChatGPT in a toaster and their stock price would jump $5/share. The sad truth is that big tech is hyper-focused on doing the things that make their stock prices go up in the short-term. It's hard to make this connection with quality initiatives. If your software is slightly less shitty, the stock price won't jump next week. So instead of being able to sell the obvious benefit of shiny new features, you need to have an Engineering Manager willing to risk having lower impact for the sake of having a better product. Even if there is broad consensus in your team, group, org that these quality improvements are necessary, there's a point up the corporate hierarchy where it simply doesn't matter to them. Certainly not as much as shipping some feature to great fanfare. Part of a bigger strategy? # Cory Doctorow has said some interesting things about enshittification in big tech: "enshittification is a three-stage process: first, surpluses are allocated to users until they are locked in. Then they are withdrawn and given to business-customers until they are locked in. Then all the value is harvested for the company's shareholders, leaving just enough residual value in the service to keep both end-users and business-customers glued to the platform." At a macro level, it's possible this is the strategy: hook users initially, make them dependent on your product, and then cram in superficial features that make the stock go up but don't offer real value, and keep the customers simply because they really have no choice but to use your product (an enterprise Office 365 customer probably isn't switching anytime soon). This does seem to have been a good strategy in the short-term: look at Microsoft's stock ever since they started cranking out AI everything. But how can the quality corner-cutting work long-term? I hope the hubris will backfire # Something will have to give. Big tech products can't just keep getting shittier—can they? I'd like to think some smaller competitors will come eat their lunch, but I'm not sure. Hopefully we're not all too entrenched in the big tech ecosystem for this to happen.

a year ago 32 votes
Coding interviews are effective

Coding interviews are controversial. It can be unpleasant to code in front of someone else, knowing you're being judged. And who likes failing? Especially when it feels like you failed intellectually. But, coding interviews are effective. One big criticism of coding interviews is that they end up filtering out a lot of great candidates. It's true: plenty of great developers don't do well in coding interviews. Maybe they don't perform well under pressure. Or perhaps they don't have time (or desire) to cram leetcode. So if this is happening, then how can coding interviews be effective? Minimizing risk # Coding interviews are optimized towards minimizing risk and hiring a bad candidate is far worse than not hiring a good candidate. In other words, the hiring process is geared towards minimizing false positives, not false negatives. The truth is, there are typically a bunch of good candidates that apply for a job. There are also not-so-great candidates. As long as a company hires one of the good ones, they don't really care if they lose all the rest of the good ones. They just need to make sure they don't hire one of the no-so-great ones. Coding interviews are a decent way to screen out the false positives. Watching someone solve coding challenges gives you some assurance that they can, well, code. Why I myself like coding interviews # Beyond why coding interviews are beneficial for the company, I actually enjoy them as an interviewer. It's not that I like making people uncomfortable or judging them (I don't), but rather I like seeing how potential future colleagues think. How do they think about problems? Do they plan their solution or just jump in? This is a person with whom I'll be working closely. How do they respond to their code being scrutinized? Do I feel comfortable having to "own" their code? On automated online assessments (OAs) # The junior developer market right now is extremely competitive and therefore it is common to use automated coding challenges (OAs) as an initial screen. OAs kind of accomplish the false positive filtering mentioned above, but that assumes candidates aren't cheating. But some are. So you're filtering your candidate pool down to good candidates and dishonest candidates. Maybe that's worth it? Additionally, OAs don't give you any chance to interact with candidates. So you get no sense of what they'd really be like to work with. All in all, I'm not a fan of OAs. Far from perfect # Coding interviews are far from perfect. They're a terrible simulation of actual working conditions. They favor individuals who have time to do the prep work (e.g., grind leetcode). They're subject to myriad biases of the interviewer. But there's a reason companies still use them: they're effective in minimizing hiring risk for the company. And to them, that's the ball game.

a year ago 36 votes

More in science

A Month In Northern Peru, Part 18: Wattled Curassows at Muyuna Lodge (February 26, 2024)

February 26, 2024 As dawn broke, Laura, Moises and I slowly cruised along a quiet watercourse. The dawn chorus was active and included species like Ferruginous Pygmy-Owl, Zimmer's Woodcreeper and Black-tailed Antbird, but I stayed focused on the treeline, hoping that a curassow-shaped bird would appear.  The dawn chorus quieted as the sun rose above the horizon. Early morning is the best time to find the curassow, and we were running out of time.  Domain of the Wattled Curassow - Muyuna Lodge, Loreto, Peru And there it was! It was the bright red bill that first caught my attention and 0.05 seconds later, my brain registered that I was staring at a female Wattled Curassow. Wattled Curassow - Muyuna Lodge, Loreto, Peru But it was better than I had imagined - the female was accompanied by a tiny chick! For a few magical minutes we watched the duo in their element.  Wattled Curassow chick - Muyuna Lodge, Loreto, Peru Wattled Curassow chick - Muyuna Lodge, Loreto, Peru Wattled Curassows are closely associated with these rivers in the western Amazon basin and that has led to their downfall. The rivers are the highways, and curassows have a lot of meat. But in a few areas like here, hunting of the curassows does not occur and encounters with this secretive species are possible. The curassows like feeding on the fruits of mahogany trees that line the watercourses, and that is likely what this female was on the hunt for.  Wattled Currasow - Muyuna Lodge, Loreto, Peru We were on cloud nine after such an epic start to the day! I was pleased that I even managed some mostly in-focus photos with my broken lens.  For the next few hours we cruised around by boat, checking out different waterways in search of some of our target birds. We were successful with Black-tailed Antbird and Short-tailed Parrot.  Black-tailed Antbird - Muyuna Lodge, Loreto, Peru Short-tailed Parrot - Muyuna Lodge, Loreto, Peru It was a very birdy morning and we cleared the hundred species mark before returning to the lodge for breakfast.  Bluish-fronted Jacamar - Muyuna Lodge, Loreto, Peru Black-tailed Tityra - Muyuna Lodge, Loreto, Peru Proboscis Bat (Rhynchonycteris nasa) - Muyuna Lodge, Loreto, Peru After breakfast, we searched by boat for some of the few remaining target birds I had in mind such as Purple-throated Cotinga, Gray-eyed Greenlet and Wing-barred Seedeater, but were unsuccessful.  Unidentified katydid - Muyuna Lodge, Loreto, Peru We were also on the lookout for Dusky-billed Parrotlets, a potential lifer for Laura and a new photographed bird for me. Moises claimed sightings on a few occasions, but each time my photos revealed that they were the more expected Riparian Parrotlets.  Band-tailed Antbird - Muyuna Lodge, Loreto, Peru At one point during the afternoon we entered an area of higher ground where there was a small walking trail. After spending most of the day in a boat it felt great to feel the earth beneath our boots, even though this meant that we were accompanied by a swarm of mosquitoes. Amazonian Trogon - Muyuna Lodge, Loreto, Peru Methona confusa - Muyuna Lodge, Loreto, Peru We found several groups of Eastern Pygmy Marmosets, one of the smallest species of monkey! Eastern Pygmy Marmoset (Cebuella niveiventris) - Muyuna Lodge, Loreto, Peru Eastern Pygmy Marmoset (Cebuella niveiventris) - Muyuna Lodge, Loreto, Peru Our guide, Moises had a stake-out for Nancy Ma's Night-Monkey (Aotus nancymai) that was visible from the main watercourse.  Nancy Ma's Night-Monkey (Aotus nancymai) - Muyuna Lodge, Loreto, Peru During the mid-afternoon, Moises took us down a channel choked with vegetation to look for some herps. We kept a close eye on the vegetation until Laura spotted the first snake, as she is known to do! Giant Parrot Snake (Leptophis ahaetulla nigromarginatus) - Muyuna Lodge, Loreto, Peru While we have seen this widespread species before, this was a new subspecies for us. The extensive black markings between the scales are quite distinctive.  Giant Parrot Snake (Leptophis ahaetulla nigromarginatus) - Muyuna Lodge, Loreto, Peru Even better than the parrot snake was this next species: a Northern Caiman Lizard (Dracaena guianensis)! I have wanted to see this huge species of lizard for many years but had never gotten lucky. As the name suggests, particularly large individuals somewhat resemble caimans, and they share similar aquatic habitats. Caiman lizards feed on snails, fish, amphibians and other creatures that they find underwater.  Northern Caiman Lizard (Dracaena guianensis) - Muyuna Lodge, Loreto, Peru Northern Caiman Lizard (Dracaena guianensis) - Muyuna Lodge, Loreto, Peru As the sun set, we found ourselves slowly paddling in a different wetland after another unsuccessful Purple-throated Cotinga search. We enjoyed watching a pair of Yellow-chinned Spinetails. This species is widespread in South America but is fairly localized in Peru.  Yellow-chinned Spinetail - Muyuna Lodge, Loreto, Peru You would be hard-pressed to find an activity that Laura likes more than a night-hike in the tropics, and tonight's walk was a good one. We only stayed out for a short while but came away with some nice sightings, including several snakes! Smoky Jungle Frog (Leptodactylus pentadactylus) - Muyuna Lodge, Loreto, Peru Ancylometes sp. - Muyuna Lodge, Loreto, Peru Unidentified net-casting spider (family Deinopidae) - Muyuna Lodge, Loreto, Peru Crowned False Boa (Pseudoboa coronata) - Muyuna Lodge, Loreto, Peru Pierella hortona - Muyuna Lodge, Loreto, Peru Leptodactylus sp. - Muyuna Lodge, Loreto, Peru Sais rosalia - Muyuna Lodge, Loreto, Peru Garden Tree Boa (Corallus hortulana) - Muyuna Lodge, Loreto, Peru I think my favourite sighting of the walk was this Slender Opossum (Marmosops sp.). It was extremely confiding and didn't mind my close approach for photos.  Slender Opossum (Marmoseps sp.) - Muyuna Lodge, Loreto, Peru Though they may resemble a rodent to some (Moises confidently identified it as an "Amazonian Mouse", whatever that is), these are actually a tiny genus of neotropical opossums, meaning that they are a marsupial. Slender opossums typically live in the viny, dense understory where they search for insects, arachnids, flowers and fruit.  Slender Opossum (Marmoseps sp.) - Muyuna Lodge, Loreto, Peru My final post from Peru will include our last couple of days at Muyuna Lodge.

23 hours ago 4 votes
Nutrition Beliefs Are Just-So Stories

But everyone wishes they weren't!

11 hours ago 2 votes
How a Problem About Pigeons Powers Complexity Theory

When pigeons outnumber pigeonholes, some birds must double up. This obvious statement — and its inverse — have deep connections to many areas of math and computer science. The post How a Problem About Pigeons Powers Complexity Theory first appeared on Quanta Magazine

yesterday 2 votes
Is Planned Obsolescence Real

Yes – it is well-documented that in many industries the design of products incorporates a plan for when the product will need to be replaced. A blatant example was in 1924 when an international meeting of lightbulb manufacturers decided to limit the lifespan of lightbulbs to 1,000 hours, so that consumers would have to constantly […] The post Is Planned Obsolescence Real first appeared on NeuroLogica Blog.

yesterday 2 votes
Why Did Korea Split?

The crazy story of a few days that changed Korea forever

yesterday 2 votes