HNHacker News
TopNewBestAskShowJobs

almostdeadguy

400 karma · joined May 5, 2018

carls junior developer
submissionscomments
almostdeadguy··on We sound like idiots when we talk about technical debt
There's (at least) three causes of technical debt that aren't due to engineering negligence (but I agree in general that many sources of "technical debt" are self-inflicted):

1. Domain modeling shifts. Engineers get a sample of the business rules for the domain they're trying to model every time they build a feature, but it's an incomplete picture. And in fact sometimes the very nature of what the business is trying to accomplish changes. That can require fundamental shifts in how the problem is modeled.

2. Changes of scale. What was engineered to support 10,000 MAU will not necessarily tolerate 100M MAU. Scale can be insidious for tech debt because it's often a slower creep of a problem w/ a high cliff in the amount of effort required to solve for it.

3. Planning/communication failures. Sometimes a critical aspect of a feature is not fully understood or articulated. Or an important delivery date for a promise is overlooked. Depending on the nature of the company's business, an early stage company often doesn't have a choice but to deliver on things that weren't properly scheduled.

almostdeadguy··on On the criteria to be used in decomposing systems into modules (1972) [pdf]
This is notably something haskell has largely been worse at than other languages in the ML family (only recently adding the backpack module system, which appears to be much less capable than the module systems in extended versions of SML and OCaml and is still not supported by tools like stack if I understand it right).

Haskell has modules, but until recently it had no module interfaces, so you could not write your code to depend on an abstract type and associated function definitions that could be swapped out dynamically (for ex: w/ mocks in testing).

Type classes are a related concept (i.e. an abstract definition of functions that may be implemented for a given type), but they enforce additional restrictions like coherency (i.e. only a single instance of class may be implemented for a given type). While this is advantageous in many situations, it's a huge pain in the ass when you need to do something like just mock network IO somewhere (as every combination of things you want to change out is going to need a newtype wrapper + class instances for all effects).

The preferred techniques for handling this the last time I used it were:

1. Transformer classes (usually w/ the "mtl" library + your own custom ones). Basically you could categorize types of useful effects into classes, use those class constraints in your function definitions + push any concrete implementations as high up the stack as possible, and use different monad transformer stacks to swap out the implementations of those effects. There's no getting around the single instance restriction of classes, but this allows you to only change out one "layer" of effects in code (say you have a transformer for network IO, you could change out only that concrete type), which reduces some of the labor involved. But there are subtle implications about the way transformers stack that change the meaning of your code, and the use of functional dependencies in MTL means you can only really use one instance of a class in your stack, so for using MTL to do something like things supply your functions w/ context/config values via MonadReader, you end up needing to smuggle around some unholy god object of everything you'd ever want to inject. Enjoy trying to write legible test cases w/ that.

2. Roll your own classes. This bypasses a lot of the weirdness you run into w/ transformer stacks, but its frankly a pain having to break out every possible effect you'll make use of into classes + defining instances for different use cases. And then you'll run into the reality that many libraries are making use of MTL-style classes so you can't entirely avoid them anyways (though you usually won't need to use MTL class constraints in your own code, and thus sidestep the aforementioned god objects).

3. Free monads. Basically describe your program as functions that don't actually run effects, but instead produce data structures describing a program that can be interpreted in several ways where the effects actually occur. This sounds awful but it's very easy to reason about as you have full control over the evaluation of effects in your interpreter functions, has a lot of nice advantages for testing + debugging as everything that could possibly cause a side effect is now inspectable as data, and is usually the least laborious of all solutions in my experience? This is known to be suboptimal from a performance perspective though.

All that said, backpack seems like it will be an improvement upon all these options, despite not having the full flexibility you get from first-class modules in other languages in the ML family tree.

One other note: 1ML looks like a very cool as a refinement of SML modules [1]. I have no real expertise w/ the design of programming languages to know if there are other problems with this approach, but I'd love to see a production-ready implementation.

[1]: https://people.mpi-sws.org/~rossberg/1ml/1ml-extended.pdf

almostdeadguy··on Tech debt gets worse before it gets better
Like everything, you need to have clarity on what you're trying to achieve and when to pull the ripcord. I'm saying you need to frame the task of a large scale refactor so that the planned implementation is falsifiable.

A rewrite is usually a quagmire if you don't do these things, but even the need for a rewrite is usually avoidable. If a team is making frequent observations about pain points and places value on fixing them quickly, they can often be avoided. But there are discontinuities: product concepts and features that dramatically change the constraints of a system late in the game, unanticipated growth requiring you to reconsider architectural decisions, etc. There's a long tail of events that you could be proactive about, but would likely create over-engineered solutions that place more limiting constraints on the design if you tried to be. They still occur though. It's very important to sufficiently interrogate the question of if a rewrite is necessary to solve these challenges, but you also need to interrogate the cost of band-aid solutions w/ limited applicability.

I don't think there's a silver bullet here, but most of the failed attempts at doing this that I've seen have been a result of teams that didn't sufficiently consider what the outcomes they were trying to provide were and how to test their candidate solution more rapidly.

almostdeadguy··on Tech debt gets worse before it gets better
I'll argue that things like this have nothing to do w/ the merits of refactoring in general and are usually due to poor project management.

A small team orchestrating a large scale refactor in isolation for over a year seems appealing to project managers because it allows that team to focus without interrupting the pace of feature development, but it makes it exceedingly likely that the output of that effort will fail for these reasons:

1. Evolving requirements. A refactor is a design that is focused on solving challenges _at that time_. Things happen over a year. A product grows, its supported feature set requires rethinking the nature of the domain, the pressures of scale force new considerations, teams get more rigorous about the kind of automated testing they practice, etc. A team that doesn't have a feedback loop into is likely to build something that may have worked for the system of a year and half ago, but no longer makes sense.

2. Lack of buy-in and knowledge sharing. In isolation, most software engineers tend to solve their own problems. Senior engineers often make assumptions about the knowledge of the rest of the team and about the coherency of their abstractions when they don't have to teach it to anyone or battle test it in any meaningful way. This is why collaborative processes like code review and pairing are often a huge win, but sharing these efforts needs to go farther and a team should be pushed to try incremental adoption and feedback until there's more confidence about a refactor. Large scale refactors should be chunked and interspersed w/ a workload that attempts to validate the success of the design (i.e. working alongside teams doing feature development to make use of the refactored design). And perhaps most importantly: the team working on this needs to have clarity on what it means to be successful. How do they know that their proposed design is meeting the needs of the team at large? Infrastructure, dev ex, or any team that is orchestrating something like this needs to think about their task like a product team, and "what does success look like" needs to be a question from the onset.

almostdeadguy··on WebAssembly: The New Kubernetes?
But to the point that K8s has "nothing a priori to do with containers" made by a sibling comment to yours, there's no reason for WASM to necessitate "the next Kubernetes". WASM is just a different workload type that could be orchestrated w/ Kubernetes. I haven't looked too much into it, but I assume that's what projects like Krustlet are working on: https://krustlet.dev/

I think that's kind of essential to understanding the role Kubernetes fulfills: it's an automation platform.

almostdeadguy··on The story behind OS X’s Unix compliant certification
Pretty sure he's widely acknowledged as a guy who was an insufferable prick to work for (or an asshole Dad in the case of his daughter). Plenty of stories of unpleasant interactions with him.

My personal favorite of the early Apple stories was the one where Jean-Louis Gassée saw him park his big dumb Mercedes in a disabled parking spot and said "I never realized those spaces were for the emotionally handicapped" [0].

[0]: https://www.folklore.org/StoryView.py?project=Macintosh&stor...

almostdeadguy··on Creating engineering principles for your team
I'm very skeptical of the idea that we can use principles to effectively instill the practices we hope to capture of a well functioning engineering team. Wittgenstein was right that language is given meaning by its use. Any principle has a context that assigns meaning to the words and sentences we use, and in an engineering culture that context is the practices of a team. When we try to lean on short principles and reiteration of them to steer our practices, we encourage a simplistic understanding of complex topics with many corner cases.

I can think of dozens of instances where dogmatic understanding of ideas like DRY, YAGNI, etc. lead to poor decisions by teams I've worked with. These are turns of phrase that have a specific context of use, but when we divorce them of that or rely on the phraseology to produce the phenomena we value, the concepts get corrupted.

almostdeadguy··on Gambler’s Fallacy and the Regression to the Mean
And that they can't open the door chosen by the contestant! Also perhaps more subtly, that Monty picks at random when given the option. The original problem prompt is _highly_ underspecified (and makes assumptions that are counter-intuitive to how a game show host might behave).
almostdeadguy··on Gambler’s Fallacy and the Regression to the Mean
The Monty Hall problem absolutely becomes more obvious when the host's behavior is explicitly documented. And as this article points out, many key aspects of the host's behavior are implausible, which is a large part of what makes it counter-intuitive: https://ima.org.uk/4552/dont-switch-mathematicians-answer-mo...
almostdeadguy··on Three Kinds of Polymorphism in Rust
One caveat to this is the "Enum with inner structs" version only avoids overhead when using individual shapes. The inner struct doesn't affect the enum size and alignment, so you still will have 12 bytes per shape in collections (8 for the largest shape variant's fields, 1 for the discriminant, and 3 for padding).
almostdeadguy··on Astronomers uncover largest group of rogue planets yet
Only tangentially related, but Melancholia is a beautiful (and uh melancholic) movie about a rogue planet that perfectly captures a prevailing mood about our present moment: https://en.wikipedia.org/wiki/Melancholia_(2011_film)

Not that we ever actually have to worry about a collision with a rogue planet.

almostdeadguy··on Mess with DNS
Great job reading the article: she's talking about frontend E2E testing which literally has nothing to do w/ the mechanics of DNS. Every one of these frameworks I've used _is_ a bit flaky too, so this should be completely unsurprising to anyone who actually knows anything about this.
almostdeadguy··on Summary of the AWS Service Event in the Northern Virginia (US-East-1) Region
> The AWS container services, including Fargate, ECS and EKS, experienced increased API error rates and latencies during the event. While existing container instances (tasks or pods) continued to operate normally during the event, if a container instance was terminated or experienced a failure, it could not be restarted because of the impact to the EC2 control plane APIs described above.

This seems pretty obviously false to me. My company has several EKS clusters in us-east-1 with most of our workloads running on Fargate. All of our Fargate pods were killed and were unable to be restarted during this event.

almostdeadguy··on Useful and useless code comments
For the most part I take no issue w/ the style of comments that act as a statement of intent.

It only becomes a problem when comments are used to paper over the author's own indifference to making the code itself legible, and that's where I expect critiques like this truly come from. Comments without legible code do not help, as the comments are not a proof of correctness or verification that the author's intent has been carried out. You still need to read the code to understand the program.

Code legibility is not unlike legibility in written language. Short sentences and paragraphs, clear transitions, well-defined hierarchy, consistent structure. Say more with less. Comments are often like footnotes: they elaborate upon the author's intent, provide a mental palette cleanser, elaborate on a digression in a way that limits distraction, or occasionally break the fourth wall.

Comments are improper when they try to serve other means. For instance comments about invalid uses of a function/class/etc. when the type system can be used to prevent illegal usages of code. Or when comments are used to explain what is happening in extremely branchy, anti-modular, or otherwise illegible code.

almostdeadguy··on Modules, Monoliths, and Microservices
Some of the connective tissue in this article could have been better written, but I think the point of talking about isolation from a security perspective is this:

A. Privilege isolation is the hardest to refute benefit of microservices in theory, but you have to keep in mind that we need hardware virtualization to achieve a reasonably strong form of that (i.e. your k8s cluster isn't helping you out here, modulo Fargate perhaps)

B. This is rarely the primary concern when we look to microservices (or generally, modularity) to cure what ails us.

C. If we have less concern about trust boundaries, there are easier ways of modularizing code that probably solve the things we're really worried about (i.e. libraries or any other way of splitting code that is built into a single artifact).

almostdeadguy··on Modules, Monoliths, and Microservices
Are the modules you're thinking of heavily shared/shaped by the needs of others? I think it's fair to say that there are more influences than just communication patterns on the design of software. For instance, there's can be a different set of concerns/pressures on developers of open source libraries than on time-sensitive and closely coordinated projects within a company.
almostdeadguy··on Modules, Monoliths, and Microservices
I think that's more or less the author's point as well. Modularity has some overlap w/ security (in the sense that we need stronger forms of modularity to get stronger forms of privilege isolation), but many of the other benefits of modularity that are usually the justification for adopting micro-services can be achieved through forms of modularity that assume trust (i.e. libraries, and other means of code splitting that may be bundled into a single unit of deployment).
almostdeadguy··on Haskell: The Bad Parts, part 2 (2020)
Easily the best reference to this I've found. I wish every language had one of these.
almostdeadguy··on Haskell: The Bad Parts, part 2 (2020)
Typically, you want to use the strict form of foldl, foldl'. The lazy version is susceptible to collecting a large number of thunks. See here: https://wiki.haskell.org/Foldr_Foldl_Foldl%27
almostdeadguy··on Losing faith in UX
The "you can always go work somewhere else" argument will never stop being a cop out. Your employer controls access to your salary, healthcare benefits, and potentially more. Leaving your job is never a completely safe move and there's an abundance of reasons why it's simply not an option to most people, and it doesn't matter what tier of middle class occupation you have for that to be true.

Moreso this argument isn't one to be taken seriously because it assumes every collective failure is a failure of millions of individual choices rather than any of the cultural inertia or environmental influences that steer our choices.

almostdeadguy··on AWS IAM is having issues again
I know a couple of ex-AWS folks who sort of say the opposite. us-east-1 is supposedly less stable because they roll out updates and new services there first.
almostdeadguy··on Dune Official Trailer
Totally agree, this looks like it'll be missing everything that's weird and wonderful about Dune.

Lynch's Dune can be bewildering coming at it without the context of the book, but that aspect of it is frankly just something you get used to with Lynch's works, and like you said its filled with so much oddness and detail. The hulking Heighliner spacecrafts, the mutated ghoulish guild navigator, the fascinating cave-like imperial palace set, the manic cyst-covered Harkonnens. I've never understood why it gets a bad rap, I think it's a hugely underrated film that did a wonderful job capturing the strange universe of Dune.

almostdeadguy··on Low Doses of LSD Acutely Increase BDNF Blood Plasma Levels in Healthy Volunteers
Taking acid to "improve your productivity at startups" is a tool for people with no appreciation for leisure or mind expansion. There's also a difference between taking something to cope or function and taking something because you think it makes you the Rembrandt of startups or whatever. I mean do what you want, and I'll reserve my right to clown on goofballs like this.
almostdeadguy··on Low Doses of LSD Acutely Increase BDNF Blood Plasma Levels in Healthy Volunteers
Utterly pretentious, dweeby reason to take acid
almostdeadguy··on Rust is not a good C replacement (2019)
If you take this argument seriously enough there's a level of specificity about the salient features of C (or any language) where you can say "nothing is a replacement for C, because it isn't C".

Is C's degree of portability important for a C replacement? Depends on the project we're talking about and the platforms it needs to support.

We can go down the line w/ these arguments (some of which like "concurrency is a bad thing" and "I don't care about safety" aren't even serious). I'm also surprised that in 2020 (or 2019 as this was written) we have engineers making a serious argument that specifications are inviolable laws. They do in fact help, but there are more considerations to make about a project's commitments to reasonable, expected behavior than just "there's a spec" (and in fact C has more guardrails supporting those commitments than a spec).

I agree that there's a rust contingent that is over-eager to rewrite and may be oblivious to the concerns of a project or broader technical implications involved in pulling rust into a project.

That being said, this reads more like a "get off my lawn", oblivious tirade about the "cult of the new" than a serious attempt to address these instances.

almostdeadguy··on Plan the Sprint, Not the Project
Months down the line your ideas might not even be relevant anymore. On my team, if you put stuff in the backlog, you're responsible for maintaining it. If you're not prepared to accept that responsibility, it belongs in a notebook or a word doc, not the backlog.
almostdeadguy··on Celebrate Woz's 70th
I went to elementary and middle school (Blossom Hill and Fisher Middle respectively) in Los Gatos, where Woz resides. I remember he donated the Mac lab at Fisher (and I think Blossom Hill too?). My memory of this is a little hazy, but I'm fairly sure Fisher had introductory programming classes that I took also, which I'm sure he had a part in. This would have been around 1999-ish when that was fairly uncommon. I wasn't a theater kid, but Woz was also well known for being a big sponsor of the theater program as well, and I remember kids in the theater program at Fisher often got cool tech gifts courtesy of Woz (like the Game Boy Color before it came out in the US).

It's probably hard to attribute this to one thing growing up in Silicon Valley in the 90s, but I consider Woz a big part of why I'm a software engineer today. And as an adult, he's one of the engineers who I most admire both professionally and personally as a kind, curious, and generous person.

almostdeadguy··on Wiki Bankruptcy
Feeling this pain a hell of a lot right now. Where I work, confluence has several simultaneous problems.

Misuse of the medium:

- It's used as a repository for product requirements (which are usually either stale, or may actually be speculative of future incarnations of a feature. In either case this is usually linked to as reference acceptance criteria in JIRA tickets, which creates all the problems you'd imagine).

- It's used to hoard information about what X team did to get something done according to another team's processes (i.e. getting secrets added to our secrets store, how to add a piece of infrastructure, etc.), which have likely changed since X team went through this (and the reference material is rarely ever linked to).

- It's used to document abstractions in code, and is usually stale as the code and documentation are not reviewed together in pull requests.

Lack of ownership/maintenance:

- All public confluence articles appear to get indexed in search (and they are by default published as public), and however search is configured it seems to be very bad at determining relevancy, so you are just as likely to get someone's stale instructions for how they followed some internal procedure as you are the owning team's documentation for doing it

- There seem to be no standards for what information needs to be provided in documentation about internal APIs. No consistency on providing information about request/response types, parameters, contextual/semantic information, limits, true SLAs (as almost everything advertised as an SLA is an SLO), downstream dependencies, etc. This may actually be a "wrong tool for the job" instance too, but in the absence of any other consolidated way of documenting APIs, this is the best thing this company has.

- No one seems to be responsible for culling stale information, and since hoarding is so prevalent, people are reluctant to actually do it. Sometimes I come across stale information in articles published by users with deactivated accounts, sometimes the only indication that the information is stale is that someone has added "(Archived)" in the title (or something similar), sometimes there's just a banner on the page saying something like "this information is no longer valid, see other team" and then linking to the wrong place to find the current information, etc. All of these pages still rank highly in search results.

There may be several organizational dysfunctions involved in creating this state of affairs, and you should have a plan for incentivizing good wiki maintenance and proper use that is a more complicated and sophisticated process, but generally I'm in favor of more radical plans to cull stale information when you find yourself in this kind of situation (if you can get buy-in). This is fundamentally different from crufty old code, because there's nothing battle-testing bad documentation like old code is battle-tested by reality, and stale information is effectively disinformation and it propagates all the usual costs of disinformation (slowed retrieval of necessary information, ill-informed decisions, etc.).

almostdeadguy··on In spite of an increase in Internet speed, webpage speeds have not improved
One of the truly differentiating factors for good software engineers is being able to recognize when your habits are in harmony with the objectives of the project you're working on. And on the meta-level, developing the sense for how to keep them in harmony with the trajectory of a project which will likely prioritize different things at different points over its lifespan.

Propensity to change is one of the most common features I've found in software projects I've worked on in my career and most software engineering "best practices", as conceived by the authors of opinions about these things, are usually strategies for managing rapid change. i.e. structuring code so it's amenable to change, understandable to the maintainer who inherits your code, has guardrails around important invariants and guarantees via assertions and tests, etc.

The details of how (and to what degree) these things should be done are highly contextually sensitive, and that is where the dogma of "best practices" can start to interfere with creating a good user experience. But I find it a little eye-rolling when people talk about hygienic software development practices and user experience as though they are in opposition. Tests, legible code, flexible structure, etc. are enablers of good user experience, because they're what allow us to change products to fit the needs of our users. They're what allow us to ship things that people can use without them exploding.

The tendency toward asset bloat on the web and just the general use of cheap-in-development-costly-for-the-user solutions (scripting languages, inefficient data structures, verbose serialization formats, piles of dependencies) is definitely an industry problem, but I think its naive to attribute these decisions to lazy devs or devs trying to make their jobs more convenient. In my experience there's two common causes for this state of things:

1. In all seriousness, the nature of capitalism. In reality, most businesses don't actually care about the majority of prospective users. They care about a couple narrow segments of users, and if those users happen to be equipped with hardware to handle this kind of inefficiency (i.e. if they're first world clients on desktop or high end mobile devices with 4G access), the business largely doesn't care. Responsiveness, low resources consumption, low energy impact, etc. are fungible engineering goals if they don't negatively affect your sales objectives.

2. The org hasn't figured out how to incentivize responsiveness, low resource utilization, etc. as objectives. Software developers get requests to focus their efforts on all different manner of criteria, and the without designing incentivization schemes and feedback loops that orient toward these objectives, there is no particular reason why they'll be inclined towards them.

almostdeadguy··on Things You Should Never Do (2000)
Currently on a project where I find myself thinking a lot about this post. I think a lot of the comments here are missing some of the finer points of this argument. "Rewriting" is not per se the problem, "rewriting from scratch" where a project is effectively put on hold until the rewrite gets done, is the real quagmire. There are legitimate ways of doing a rewrite that don't require boiling the ocean and prevent you from shipping. And I'm sure there are exceptions as well, but as a general piece of advice about software engineering I've found this to have a better shelf life than many other nuggets of advice.
← PreviousPage 7 of 10Next →