A simpler way to do this, especially if you do tagging in your repositories, is to use `git describe`. For example:
$ git describe --dirty
v1.4.1-1-gde18fe90-dirty
The format is <the most recent tag>-<the number of commits since that tag>-g<the short git hash>-<dirty, but only if the repo is dirty>.
If the repo isn't dirty, then the hash you get excludes that part:
$ git describe --dirty
v1.4.1-1-gde18fe90
If you're using lightweight tags (the default) and not annotated tags (with messages and signatures and etc) you may want to add `--tags` because otherwise it'll skip over any lightweight tags.
The other nice thing about this is that, if the repo is not -dirty, you can use the output from `git describe` in other git commands to reference that commit:
$ git show -s v1.4.1-1-gde18fe90
commit de18fe907edda2f2854e9813fcfbda9df902d8f1 (HEAD -> 1.4.1-release, origin/HEAD, origin/1.4.1-release)
Author: rockowitz <[email protected]>
Date: Sun May 28 17:09:46 2023 -0400
Create codacy.yml
Here is my cynical take in ci. Firstly, testing is almost never valued by management which would rather close a deal on half finished promises than actually build a polished, reliable product (they can always scapegoat the eng team if things go wrong with the customer anyway).
So, to begin with, testing is rarely prioritized. But most developer orgs eventually realize that centralized testing is necessary or else everyone is stuck in permanent "works on my machine!" mode. When deciding to switch to automated ci, eng management is left with the build vs buy decision. Buy is very attractive for something that is not seriously valued anyway and that is often given away for free. There is also industry consensus pressure, which has converged on github (even though github is objectively bad on almost every metric besides popularity -- to be fair the other larger players are also generally bad on similar ways). This is when the lock in begins. What begins as a simple build file starts expanding outward. Well intentioned developers will want to do things idiomatically for the ci tool and will start putting logic in the ci tool's dsl. The more they do this, the more invested they become and the more costly switching becomes. The CI vendor is rarely incentivized to make things truly better once you are captive. Indeed, that would threaten their business model where they typically are going to sell you one of two things or both: support or cpu time. Given that business model, it is clear that they are incentivized to make their system as inefficient and difficult to use (particularly at scale) as possible while still retaining just enough customers to remain profitable.
The industry has convinced many people that it is too costly/inefficient to build your own test infrastructure even while burning countless man and cpu hours on the awful solutions presented by industry.
Companies like blacksmith are smart to address the clear shortcomings in the market though personally I find life too short to spend on github actions in any capacity.
(except I have it aliased to "jq-structure" locally of course. also, if there's a new fancy way to do this, I'm all ears; I've been using this alias for like... almost a decade now :/)
In the spirit of trying out jqfmt, let's see how it formats that one-liner...
Not bad! Shame that jqfmt doesn't output a newline at the end, though. The errant `%` is zsh's partial line marker. Also, `-ob -ar -op pipe` seems like a pretty good set of defaults to me - I would prefer that over it (seemingly?) not doing anything with no flags. (At least for this sample snippet.)
Defining async is hard. And I'm writing this as one of the many people who designed async in JavaScript.
I don't quite agree with the definition in this post: just because it's async doesn't mean that it's correct. You can get all sorts of user-land race conditions with async code, whether it uses `async`/`await` (in languages that need/support it) or not.
My latest formulation (and I think that it still needs work) is that async means that the code is explicitly structured for concurrency.
Recently I was introduced to the distinction between anxiety and dread. Anxiety is, essentially, a form of fear. You fear a worst-case consequence that isn't actually that likely. If you put up with your anxiety and just go and do the thing (on average) you'll do just fine, or at least ok-ish. Over time your body learns that the anxious activity is ok and the anxiety is reduced.
Dread is different. Dread is the expectation of a bad situation. It's not a worst-case scenario, it's a typical scenario. If what you are experiencing is dread, then pushing yourself into that situation will confirm to your body that, yup, it really is as bad as you thought, and will amplify the dread rather than diminish it.
A classic example is that certain forms of neurodivergence create sensory overload in typical "social" environments. This is likely to result in dread rather than anxiety. Your body is literally telling you that this situation is problematic, and repeat exposure isn't going to improve anything.
In our modern culture the language of anxiety is widespread but the language of dread much less so, and I think that's unfortunate because a lot of advice centers around "just get over it", which works only if what you're experiencing is anxiety. Personally, learning about this gave me permission to do "social" activities on my own terms and stop worrying about what other people think "social" means; turns out the social anxiety I had was relatively minimal and what I was experiencing was mostly the dread from environments where social activities often occur.
My most recent example of this is mentoring young, ambitious, but inexperienced interns.
Not only did they produce about the same amount of code in a day that they used to produce in a week (or two), several other things made my work harder than before:
- During review, they hadn't thought as deeply about their code so my comments seemed to often go over their heads. Instead of a discussion I'd get something like "good catch, I'll fix that" (also reminiscent of an LLM).
- The time spent on trivial issues went down a lot, almost zero, the remaining issues were much more subtle and time-consuming to find and describe.
- Many bugs were of a new kind (to me), the code would look like it does the right thing but actually not work at all, or just be much more broken than code with that level of "polish" would normally be. This breakdown of pattern-matching compared to "organic" code made the overhead much higher. Spending decades reviewing code and answering Stack Overflow questions often makes it possible to pinpoint not just a bug but how the author got there in the first place and how to help them avoid similar things in the future.
- A simple, but bad (inefficient, wrong, illegal, ugly, ...) solution is a nice thing to discuss, but the LLM-assisted junior dev often cooks up something much more complex, which can be bad in many ways at once. The culture of slowly growing a PR from a little bit broken, thinking about design and other considerations, until its high quality and ready for a final review doesn't work the same way.
- Instead of fixing the things in the original PR, I'd often get a completely different approach as the response to my first review. Again, often broken in new and subtle ways.
This lead to a kind of effort inversion, where senior devs spent much more time on these PRs than the junior authors themselves. The junior dev would feel (I assume) much more productive and competent, but the response to their work would eventually lack most of the usual enthusiasm or encouragement from senior devs.
How do people work with these issues? One thing that worked well for me initially was to always require a lot of (passing) tests but eventually these tests would suffer from many of the same problems
Fallout 3 is the best example of uncanny valley phenomenon.
In original Fallout you enter a city which spans across four screens, repetetively uses the same set of limited sprites and is inhabited by some random moving pixels acting as citizens of which several have some interesting dialog lines.
The Fallout 3 seems similar at first, only employing a major leap in technological advancement - the city spans in every direction, there are animated and voice acted characters, and everyone is presented in cutting edge 3D technology.
But it all falls flat upon closer inspection. Every building is exactly the same. Dialogs are mundane and everyone has some sort of hiking accident where they took an arrow to the knee and even though the city has only 8 buildings its easy to get lost.
The reason is simple - in the original the story takes place mainly in your head, it's a mind's eye theatre, the game only gives you a broad outlines of the world and you are forced to fill out the blanks yourself. The game doesn't try to be realistic and leaves you a lot of space.
Once you start animating and voice acting your characters you quickly realize it's an endless money pit. The more lines you add, the cost grows exponentially, because you have to account for all localisation scenarios.
This is summed up very well (forgive me for not looking up reference and quoting from memory) by Bioshock lead who said in interview:
"At work if I have an idea we need to assemble a team - including programmers, modellers, scripters - and just to get a basic prototype that would give us the gist of what we were thinking it takes a month or more. Meanwhile, at home, I can relax after work, and in one evening session create a whole Doom campaign."
They’ve got the weird shit covered still, apparently the joy cons in this gen can be used as mice.
Was heavily rumoured/leaked and this teaser video literally shows them gliding along a surface.
How Nintendo will leverage that functionality, who could honestly say, but that’s the genius of keeping a toy company mindset in an industry full of sports car company mindsets.
It's simple: "string" literals in C++ are pointers to characters. std::vector has a constructor which takes a begin and end iterator and creates a vector with contents copied from that range. Pointers are iterators in C++, and chars are implicitly convertible to ints, so overload resolution selects the aforementioned constructor. The pointer to "1" and the pointer to "2" don't point into the same object, so trying to iterate from one to the other eventually causes a dereference past the end of "1" which is undefined behavior.
While these are often useful for setting some internal guidelines for promotions, I've found such an approach to usually end up creating a giant inverse bell, where one side of the curve is constrained by the framework while the other side gamifies it. In the middle, there is a few folks that actually pass the bar organically, and those usually end up being the best hires.
Why? Well, one side is often constrained by the red tape.
i.e. "Oh for this promotion you have to define and deliver technical roadmaps of larger projects, we don't see you having done that in the last 6 months.", meanwhile the person is assigned to a team where their lead specifically does these things and will not be provided a chance to do it, or they have another person on the team vying for the same position which will jump on the task.
This leads to people being stuck in the same level for a while, until they decide to jump ship because there is less energy required to jump to/into that level in another company than it is to untangle the red tape at their current place.
The other group gamifies the system as much as possible. While one could say that is a good thing (no project to lead? create one!), it often ends up with folks doing fluff work just to show it off on their promotion review, coming up with project that will be left to die as soon as they get that jump or overestimating the value of their current work and impact, i.e.:
"now see, that event notification _notification_ delivery service topology I was working on the last 3 months was super important because there is a percentage of cases where they were not delivered on time, which is terrible, right? but our event notification delivery microservice doesn't deal with that, so now that we have an established topology we can architecture a backing service to pre-notify us of misdelivered messages and be sure it will work." (Jim, developer in a series A startup with 200 users)
While there is excellence hidden here sometimes (great operators will do whatever the hell they think needs to be done, red tape or not), often times it is just political grifting, trying to one-up the system to get into a position of more power/money/influence. These folks usually end up creating more red tape to constraint others from doing the same, or burn the bridges while they cross them, causing impact to the company (wasted time, wasted money, wasted code) for their own gain.
For non-experienced people, recognizing these is often hard, while even for the experienced ones fighting them is quite hard too, as they are playing the system and will use it to their defense.
The best way to achieve a good abstraction is to recall what the word meant before computer science: namely, something closer to generalization.
In computing, we emphasize the communicational (i.e. interface) aspects of our code, and, in this respect, tend to focus on an "abstraction"'s role in hiding information. But a good abstraction does more than simply hide detail, it generalizes particulars into a new kind of "object" that is easier to reason about.
If you keep this in mind, you'll realize that having a lot of particulars to identify shared properties that you can abstract away is a prerequisite. The best abstractions I've seen have always come into being only after a significant amount of particularized code had already been written. It is only then that you can identify the actual common properties and patterns of use. Contrarily, abstractions that are built upfront to try and do little more than hide details or to account for potential similarities or complexity, instead of actual already existent complexity are typically far more confusing and poorly designed.
Events are published observations of facts. If you want to be able to use them as triggers, or to build state, then you have to choose the events and design the system to make that possible, but you always have to ensure that systems only publish facts that they have observed.
Most issues I've seen with events are caused by giving events imprecise names, names that mean more or less than what the events attest to.
For example, a UI should not emit a SetCreditLimitToAGazillion event because of a user interaction. Downstream programmers are likely to get confused and think that the state of the user's credit limit has been set to a gazillion, or needs to be set to a gazillion. Instead, the event should be UserRequestedCreditLimitSetToAGazillion. That accurately describes what the UI observed and is attesting to, and it is more likely to be interpreted correctly by downstream systems.
In the article's example, SeatSelected sound ambiguous to me. Does it only mean the user saw that the seat was available and attempted to reserve it? Or does it mean that the system has successfully reserved the seat for that passenger? Is the update finalized, or is the user partway through a multistep process that they might cancel before confirming? Depending on the answer, we might need to release the user's prior seat for other passengers, or we might need to reserve both seats for a few minutes, pending a confirmation of the change or a timeout of their hold on the new seat. The state of the reservation may or may not need to be updated. (There's nothing wrong with using a name like that in a toy example like the article does, but I want to make the point that event names in real systems need to be much more precise.)
Naming events accurately is the best protection against a downstream programmer misinterpreting them. But you still need to design the system and the events to make sure they can be used as intended, both for triggering behavior and for reporting the state and history of the system. You don't get anything automatically. You can't design a set of events for triggering behavior and expect that you'll be able to tell the state of the system from them, or vice-versa.
The fundamental reason for this is simple. Humans are prone to cognitive dissonance. Meaning, we do absurd things to avoid painful thoughts. And anything that questions our sense of identity, is a painful thought.
So if my self-image is, "I've advanced our understanding of the fundamental nature of reality," then the idea that my contributions weren't useful becomes painful. So we avoid thinking it, challenge people who question our past contributions, and so on.
The natural result of this cognitive dissonance is a feeling of undue certainty in our speculations. After all certainty is merely a belief that one idea is easy to believe and its opposites are hard to believe. We imagine that our certitudes are based on fact. But they more easily arise from cognitive biases.
And this is how a group of intelligent and usually rational people descend into theology whose internal contradictions can't be acknowledged.
This entire blog post assumes agency that you, the EM or Team lead, rarely have in an organisation that is on a "wartime" footing. It's cosplay.
Real wartime footing:
1. Direction and technical decisions are driven by priorities of board-level members and often arrive in email form late on Friday evening. The entire organisation is expected to pivot immediately. A new senior leadership team member starts scheduling daily read-outs on project progress, and half the organisation spends the weekend frantically hallucinating project plans into Google Sheets.
2. Engineering staff react with dull-eyed disbelief on Monday; they knew this was coming, because the same thing happened a month ago, and six weeks before that.
3. Emails come from HR that there are are new, even-more-labyrinthine approval processes for expenses, and shrinking budgets for anything not directly related to whatever the projet du jour, which will be fed enough to make it look like it's succeeding until the next Friday evening email kills it.
4. There is wide-spread burn-out across Engineering teams, and people are reduced to reactive, sarcastic automatons.
5. A creeping understanding seizes the better engineers that things cannot improve; they sign articles of Armistice, pretend to comply, and start interviewing elsewhere.
6. An email arrives on Friday night...
> Focus on the positive aspects of the job that can be taken for granted, like the opportunity to work on cutting-edge challenges, the company's still existing perks and benefits, the amazing team you have, the chance to work with a modern tech stack, or how your product is helping its users. Showing your team how you appreciate what's still good can help with morale.
If HN supported gifs, there'd be several in this spot.
Maybe they have saved enough from working in tech that they can grow vegetables for themselves in a very low scale way. Its nice to escape from the career you've had for decades. Sometimes its not even an escape from the career, but the career and the city you've lived in. Moving to the forest and growing some vegetables and raising chickens isn't that difficult. You certainly don't need "millions in equipment". Its exactly what I did.
I found it difficult to get a job in tech at the start of COVID after working in it for ~25 years. I moved to Michigan, and now live in the woods. My Cost of Living is a fraction of what it was. My mortgage is only 80% of what I was paying for rent in the SFBay area. Its peaceful and quiet here. It actually gets dark too. I no longer hear BART screeching on the rails at 2am or the constant flow of traffic. I.. do once again work in tech though at a much 'smaller' scale. My company is small and work demands don't dominate my life. I have balance.
This year I've planted ~200 onions, ~100 potatoes, ~100 garlic, ~60 strawberry. I have blueberry from a few years back starting to flourish. I have wild blackberry, and mushrooms galore. "touching grass" is a daily activity as we manage our small flock of chickens.
> I bet that WhatsApp is one of the rare services you use which actually deployed servers to Australia. To me, 200ms is a telltale sign of intercontinental traffic.
So, I used to work at WhatsApp. And we got this kind of praise when we only had servers in Reston, Virginia (not at aws us-east1, but in the same neighborhood). Nowadays, Facebook is most likely terminating connections in Australia, but messaging most likely goes through another continent. Calling within Australia should stay local though (either p2p or through a nearby relay).
There's lots of things WhatsApp does to improve experience on low quality networks that other services don't (even when we worked in the same buildings and told them they should consider things!)
In no particular order:
0) offline first, phone is the source of truth, although there's multi-device now. You don't need to be online to read messages you have, or to write messages to be sent whenever you're online. Email used to work like this for everyone; and it was no big deal to grab mail once in a while, read it and reply, and then send in a batch. Online messaging is great, if you can, but for things like being on a commuter train where connectivity ebbs and flows, it's nice to pick up messages when you can.
a) hardcode fallback ips for when DNS doesn't work (not if)
b) setup "0rtt" fast resume, so you can start getting messages on the second round trip. This is part of noise pipes or whatever they're called, and tls 1.3
c) do reasonable-ish things to work with MTU. In the old days, FreeBSD reflected the client MSS back to it, which helps when there's a tunnel like PPPoE and it only modifies outgoing syns and not incoming syn+ack. Linux never did that, and afaik, FreeBSD took it out. Behind Facebook infrastructure, they just hardcode the mss for i think 1480 MTU (you can/should check with tcpdump). I did some limited testing, and really the best results come from monitoring for /24's with bad behavior (it's pretty easy, if you look for it --- never got any large packets and packet gaps are a multiple of MSS - space for tcp timestamps) and then sending back client - 20 to those; you could also just always send back client - 20. I think Android finally started doing pMTUD blackhole detection stuff a couple years back, Apple has been doing it really well for longer. Path MTU Discovery is still an issue, and anything you can do to make it happier is good.
d) connect in the background to exchange messages when possible. Don't post notifications unless the message content is on the device. Don't be one of those apps that can only load messsages from the network when the app is in the foreground, because the user might not have connectivity then
e) prioritize messages over telemetry. Don't measure everything, only measure things when you know what you'll do with the numbers. Everybody hates telemetry, but it can be super useful as a developer. But if you've got giant telemetry packs to upload, that's bad by itself, and if you do them before you get messages in and out, you're failing the user.
f) pay attention to how big things are on the wire. Not everything needs to get shrunk as much as possible, but login needs to be very tight, and message sending should be too. IMHO, http and json and xml are too bulky for those, but are ok for multimedia because the payload is big so framing doesn't matter as much, and they're ok for low volume services because they're low volume.
> if you want to succeed as a Data Scientist and be praised by management - you got to provide data analysis that supports managements ideas (however wrong or ineffective they might be).
> Data Scientist's job is to launder management's intuition using quantitative methods :)
It’s no different than the days when grey bearded wisemen would read the stars and weave a tale about the great glory that awaits the king if he proceeds with whatever he already wants to do.
The beards might be a bit shorter or nonexistent, but the story hasn’t changed.
There's a document that I think was an unofficial project management document from NASA, I can't find it now, but one of the items was something along the lines of 'if your space mission isn't reusing an existing launch vehicle, it will be a launch vehicle development project and everything else, including what you think is the focus of your mission, will be secondary to this'.
Edit: additional bit I remembered, this statement is prefixed with something like 'you may start thinking you project will be cheaper/more efficient if you develop a launch vehicle suited to your particular mission instead of compromising on an existing one, but', then the above.
...obviously, in some cases a graph/hierarchy is very useful to be able to traverse, but emitting records and "coalescing" the path into a record-field makes simple operations simple.
Don't make me go like `.cpu.bank[0].chip[3].temp` or whatever... just give me "all the CPU's" and then give me a good URI(!!) to describe it or search for it later.
Rationale: coalescing and traversing weird object paths is tough to do dynamically via current shell DSLs, and the URI concept as an ID excellent application.
I tend to build the "ownership" model whenever I can. It works extremely well and has a few simple rules:
1. a user can own an entity/row/unit/whatever. They have full control over this unit.
2. a user can share ownership with another user/role.
3. a user can share various "rights" over any units they own -- CRUD, for example -- for any user/role.
4. a user can only interact with any unit they have a right to.
This can be implemented through a simple db table (or inline in the data itself) and doesn't depend on much. Once you build the middleware, you don't even need to think about the authorization layer.
…The paper found that, in terms of sheer number of syllables spoken per second, the fastest languages of the 17 studied were Japanese, Spanish, and Basque. The slowest were Cantonese, Vietnamese, and Thai.
…
What Pellegrino found is that, essentially, all languages convey information at roughly the same speed when all the factors are taken into account: around 39 bits per second. The higher the syllable-per-second rate, the lower the information density, which creates a trade-off that makes all languages around the same in terms of information rate.
So if you wanted to communicate the most quickly, you should rapidly speak in a more dense language like Cantonese or Vietnamese.
I believe what the author is describing is called A Desktop Application. They were these crazy forms of web apps, that ran on a local computer, stored data locally, and didn't use a server. Or a web browser. Legend has it that they used little memory, were fast and snappy, and enabled native integration with all of an operating system's capabilities, widgets, etc, while still allowing a user to completely control what happens with the data the program used.
Porting this type of application could take a lot of work! So at some point, somebody invented a programming language called Java, which used a thing called a Virtual Machine, so that one application could run the same way on any computer that could run the Java Virtual Machine. They called this A Portable Desktop Application.
Unfortunately, this obscure language was somewhat difficult to use, so this paradigm was abandoned for simpler languages, and with it, so went the idea of A Portable Desktop Application.
Decades later, somebody reinvented the idea. But it required a much more limited application platform, running inside a kind of hacked-together document viewer, with a simplistic programming language designed to control how documents were displayed. It took about 1000x as much memory and CPU, but, with addition of about 50 different frameworks and 30 different new custom APIs, you could finally get close to the same functionality as the first solution.
There are two types of github actions workflows you can build.
1) Program with github actions. Google "how can I send an email with github actions?" and then plug in some marketplace tool to do it. Your workflows grow to 500-1000 lines and start having all sorts of nonsense like conditionals and the YAML becomes disgusting and hard to understand. Github actions becomes a nightmare and you've invited vendor lock in.
2) Configure with github actions. Always ask yourself "can I push this YAML complexity into a script?" and do it if you can. Send an email? Yes, that can go in a script. Your workflow ends up being about 50-60 lines as a result and very rarely needs to be changed once you've set up. Github actions is suddenly fine and you rarely have to do that stupid push-debug-commit loop because you can debug the script locally.
Every time I join a new team I tell them that 1 is the way to madness and 2 is the sensible approach and they always tepidly agree with me and yet about half of the time they still do 1.
The thing is, the lack of debugging tools provided by Microsoft is also really not much of a problem if you do 2, vendor lock in is lower if you do 2, debugging is easier if you do 2 but still nobody does 2.
If the repo isn't dirty, then the hash you get excludes that part:
If you're using lightweight tags (the default) and not annotated tags (with messages and signatures and etc) you may want to add `--tags` because otherwise it'll skip over any lightweight tags.The other nice thing about this is that, if the repo is not -dirty, you can use the output from `git describe` in other git commands to reference that commit: