HN user

wfleming

1,264 karma

https://gitHub.com/wfleming

Posts1
Comments167
View on HN

Custom furniture/cabinetry is already a pretty tough market, and woodworking is such a common programmer hobby that if a significant chunk of us decided to make a go of it the market would get heavily oversupplied pretty fast :).

I’ve had people tell me I should try selling some of the furniture I make and my response is always that I made the mistake of turning a hobby into a career once, I don’t intend to make that mistake again, and at least software still pays pretty well.

As I understand it, "to be trespassed" is a term of art that basically means "the cops were called, told that person was trespassing, the cops duly informed that person they are trespassing & had to leave the property, and the person left, but was not charged". It's basically establishing a legal trail so that if the person refuses to leave or continues to trespass at that location in the future they have a better basis for charging them.

I also use pass. Any forge you feel like is fine (I use gitlab). I backup my gpg key with `gpg —export-owner-trust` and store that backup elsewhere.

Pass has a pretty good ecosystem of plugins/other clients, as well. There are open source iOS/Android clients and browser extensions so once you’re setup the day-to-day experience is not far off from any of the popular hosted password managers.

My only real issue is the dependency on gpg, as it’s pretty long in the tooth and a hassle to operate. (If you are not comfortable using gpg, spend some time learning that before you go all-in on pass!) There’s a fork[1] which swaps gpg for age, but it hasn’t attracted enough attention to get a similar ecosystem of mobile clients/browser extensions, so it’s not a very practical choice IMHO.

[1] https://github.com/FiloSottile/passage

I'm with you, but I do think the situation can be characterized differently in a couple important ways:

1. IE was the default browser for many users (i.e. anybody using Windows who didn't know better).

2. IE had a lot of bugs and and was often non-compliant with standards.

Those two things combined meant that supporting IE required additional work, and if you didn't put in that work you were going to get users from IE anyway they'd just get frustrated and confused when things broke. So "detect IE and tell them use something else" was at least a reasonable fixed-cost approach to not having users get totally stuck. (And IE went down to 2-3% at least in part because devs revolted against IE earlier and started serving those "don't use IE" messages when its usage was still higher.)

Neither factor is really true of FF. It's not the default for any major platform, its user-base at this point is largely power users who won't be easily confused, and outside of some non-standard APIs most sites don't need and some fairly edge-casey stuff, most sites that work on Chrome will work fine on FF as well without alteration. If anything, IME Safari is more likely to need special attention than FF (but of course Safari has much higher market share so it merits that effort).

So I totally get not wanting to spend QA budget on FF, and I could understand showing a small banner suggesting you use a different browser, but erroring/completely blocking usage of the site does feel excessive to me, and even a bit mean-spirited since it takes extra effort to detect FF to show the message and prevent using the site! I don't think these sites are going out of their way to block usage of other low-usage browsers (some of which can alter behavior that could break some sites even if they are Chromium-based).

We’re in very nitpicky terminology weeds here (and I’m not the person you’re replying to), but my understanding is “commutative” is specifically about reordering operands of one binary op (4+3 == 3+4), while “associative” is about reordering a longer chain of the same operation (1+2+3 == 1+3+2).

Edit: Wikipedia actually says associativity is definitionally about changing parens[0]. Mostly amounts to the same thing for standard arithmetic operators, but it’s an interesting distinction.

[0] https://en.wikipedia.org/wiki/Associative_property

Adding non-primitive props you get passed into your component to internal dependency arrays is rarely right, because this component has no control over the referential stability of those props.

I think this is wrong? If you memoize a callback with useCallback and that callback uses something from props without putting it in the dependency array, and then the props change & the callback runs, the callback will use the original/stale value from the props and that's almost certainly a bug.

They might be trying to say you just shouldn't use useCallback in that situation, but at best it's very confusingly written there because it sure sounds like it's saying using useCallback but omitting a dependency is acceptable.

IMHO useCallback is still a good idea in those situations presuming you care about the potential needless re-renders (which for a lot of smaller apps probably aren't really a perf issue, so maybe you don't). If component A renders component B and does not memoize its own callback that it passes to B as a prop, that is A's problem, not B's. Memoization is easy to get wrong by forgetting to memoize one spot which cascades downwards like the screenshots example, but that doesn't mean memoization is never helpful or components shouldn't do their best to optimize for performance.

The “stolen” one is a 1991 model according to the article, so not that new (just kept in immaculate condition as the photos show, I was surprised when I saw it was a ‘91), and only 6 years between the two vehicles involved. Given those ages, it’s not shocking Mercedes was using the same key patterns.

I can think of multiple “corner stores” that are the only business within a single-family home residential area within a few minutes drive of the house I grew up in in suburban NY. I’m pretty sure they all got grandfathered in and would not be permitted as new construction with the zoning, but they’ve all been in business since before I was born and are still going. These are mostly neighborhoods without sidewalks, and the stores have parking for only a handful of cars.

You’re right that “most” houses can’t be within walking distance of a corner store outside cities, but my anecdata experience is those residential communities can definitely support those businesses. They might require a short drive, but they’re still a lot closer than the shopping center, and a mix of “ran out of one thing”, deli/breakfast sandwiches, and beer keeps them in business.

Vlovich is describing the very last race only, where he did place the final bet. He takes her 4k and tells her he’s going to place the bet for her so how much she’ll win is a surprise. The horse he said he would bet on loses the race and then he reveals he actually bet on the winner. So for that race and that race only I do think he just bet on every horse and sleight-of-handed the right ticket to her. Timestamp 35:20 in the video you linked earlier. Right after that he explains how the “trick” worked for all the earlier races, which is what you’re referring to.

I believe `makemigrations` builds up its conception of "what the schema is" from the `CreateModel`, `AddField`, `AlterField`, etc. ops in the migrations files. But it doesn't incorporate `RunSQL` ops into building that model of the schema. If my migrations were just a bunch of `RunSQL` ops, I think `makemigrations --dry-run` would basically just see everything from models.py as always needing to be added.

This behavior is why `SeparateDatabaseAndState` is a necessary hack in Django: sometimes you need to do an `AlterField` where the SQL Django would generate is really bad, so you need to write your own `RunSQL` to do the right thing, but you also need Django to see the `AlterField` as applied or you'll have problems with future migrations.

I suppose I could modify my preference to "run makemigrations and then wrap every single op in SeparateDatabaseAndState", but that does not sound fun :).

I prefer null as a clear “no value” marker to handle rather than special-casing handling of an empty string vs other strings, and if “empty string” isn’t considered valid that should be validation logic and that should never get stored in the database. But it’s certainly a matter of opinion and either can work as long as you’re consistent.

The point about Django’s choices about default and not-null though is that it can easily lead to crashes while you’re adding fields. If you add a string field with default=“” and don’t specify null=False, the generated schema will be a non-null field without a default value in SQL, but Django will backfill “” into all existing rows. To avoid downtime, you need to deploy migrations & apply them, then deploy the models.py/other code changes. But if anyone tries to write a new row before the new code finishes deploying after applying the migration, it will crash.

Migrations in Django The Django approach has noteworthy differences and a slightly different workflow

My explanation of Django's approach to migrations would involve a lot more expletives. It is by far my least favorite thing about the framework.

- Fields are not-null by default, the opposite of SQL itself

- Field declarations use argument names that sound like the SQL equivalents (default, on delete cascade/restrict), but they're fully enforced in python and don't make it into the SQL schema at all by default. I get that not every DB supports these features, and there are sometimes good reasons for doing something like a delete cascade in process (if you need to implement custom on-delete logic or something), but the DSL shouldn't read like SQL if it's not going to change the SQL schema. The default value thing combined with not-null by default is particularly easy to get bitten by adding a new field with default if you deploy migrations separately from deploying code (which you must do to avoid downtime): if you add a new field with a default value believing it's safe, you will probably get crashes in the interval between applying the migration & the new code deploying because you've got not-null column without a default value in the schema. They did finally add db_default recently, thankfully, but it took years!

- Django migrations cannot be understood in isolation, the sql a generated migration containing something like an AlterField operation will run depends on what earlier migrations say. You have to check with the sqlmigrate command and/or actually read earlier migrations to be sure you understand what the migration will do. Compared to Rails, where each migration can be read and understood in isolation (though you may still need to understand how a Rails migration DSL will translate to actual SQL of course). This also has a performance impact making Django migrations slower because Django has an in-memory model of what the schema should be at each migration point, so running a migration is not just "load the file and run the commands", it's "determine the schema that should exist after running this migration, diff that with the schema we think exists before this point, magically generate SQL for that diff, then run that".

- The makemigrations command to auto-generate pending migrations is very aggressive and will detect things as "changed" that don't impact the schema. If you changed some help text on a field, or a localization string, or the aforementioned only-in-python default value, makemigrations will see that as requiring a migration that does nothing. Leads to lots of cruft.

- Related to both of the above points, AlterField's auto-generated SQL can be dangerous and bad. Particularly, I've seen cases where very minor changes to a ForeignKey (like changing from nullable to not-nullable, or even not-schema-impacting changes like above) would, by default, have dropped the foreign key constraint & index, and then re-created them. Completely unnecessary and potentially dangerous since it could be locking a large table. I'm not positive, but in some cases I think these have been generated purely because of a django upgrade leading to it deciding the names of the indexes/constraints need to be changed for some reason.

- AlterField will also tend to stomp all over any tweaks you manually made to the schema to work around all these issues. If you manually wrote a SQL statement in an earlier migration to add a default value to a column, and then that column's definition gets tweaked months or years later the generated AlterField is gonna remove your default value. At a technical level this isn't surprising when you understand how Django is modeling the schema internally & generating the SQL changes, but it's definitely a bad user experience downstream of a lot of these design decisions.

Generally the field declaration/migrations system in Django feels to me designed to lead people down a garden path towards bad and dangerous behavior. If I had my druthers I'd enforce a policy in our Django app of "never run makemigrations, all migrations must be manually written SQL".

The trouble is the justification of a subscription is evaluated differently by businesses and customers, and both perspectives are rational. If you’ve got servers to pay for, subscriptions are a very appealing model since it makes the “is this business sustainable” math very easy (and less charitably lots of businesses are after that sweet sweet subscription revenue because it tends to be sticky). As a customer, I think it’s also very reasonable to get annoyed that “everything is becoming a subscription” and say “why would I pay this much for something I might need once in a blue moon.”

It is useful for the geography to be at least close-to-correct, because step 1 of navigating a transit map is deciding if transit is the best way to go. The tube map is well known for having spots that make this deceptive, e.g. tourists would take the tube from Bank to Mansion House, although it would often be faster to walk.

Very much agree. I have built infra on ECS with terraform at two companies now, and we have zero manual steps for actions like this, beyond “add the env var to a terraform file, merge it and let CI deploy”. The majority of config changes we would make are that process.

Python with Braces 2 years ago

Probably because that makes parsing much more complicated, and this being a half-joking hobbyist project they didn’t want to increase scope that much.

I think the takeaway of how to succeed with this kind of project is good, but a missing element for why so many kind of never take off that I haven’t seen mentioned yet is that a lot of times the compatability of these alternative implementations is in practice lower than claimed even for old language features. E.g. it’s very common for Ruby and Python apps to have some native C extensions somewhere in their dependencies, and AFAIK the major alternative implementations have never supported them. (They’ve tried sometimes, but it’s never really worked out for obvious technical reasons, and the alternative of expecting libraries to provide multiple implementations has a rocky history as well.)

Combine that with the fact these languages are often used to make CRUD websites where I/O is likely to be a bigger performance factor than CPU and the faster alternatives look a lot less appealing.

Also, when ranked by that total the “best 4” aren’t really the best 4. They don’t report rape numbers, so they don’t get a total. And their numbers are all worse than NY on the other categories (murder, robbery, assault).