HN user

mwilliamson

469 karma

I'm a software developer based in Cambridge, UK. Feel free to get in touch.

E-mail: hello@zwobble.org

Blog: http://mike.zwobble.org/

GitHub: http://github.com/mwilliamson

Posts19
Comments59
View on HN
github.com 2y ago

Show HN: Mintaka: Run long-running processes, automatically focus on problems

mwilliamson
2pts0
mike.zwobble.org 4y ago

My tiny side project has had more impact than my decade in the software industry

mwilliamson
701pts189
github.com 9y ago

Show HN: GraphJoiner: Implementing GraphQL with joins

mwilliamson
3pts0
github.com 11y ago

Farthing: generate type annotations for Python source code by running it

mwilliamson
1pts0
mike.zwobble.org 11y ago

Nope: a statically-typed subset of Python that compiles to JavaScript and C#

mwilliamson
1pts0
news.ycombinator.com 12y ago

Ask HN: How can technology improve education?

mwilliamson
6pts2
www.simple-talk.com 12y ago

Moving from Practice to Production with Test-Driven Development

mwilliamson
1pts0
mwilliamson.github.io 12y ago

Mammoth: convert .docx documents to simple and clean HTML

mwilliamson
1pts0
mike.zwobble.org 12y ago

Converting docx to clean HTML: handling the XML structure mismatch

mwilliamson
2pts0
mwilliamson.github.io 12y ago

Language Ignition - Reference for managing programming language environments

mwilliamson
2pts0
github.com 12y ago

Whack: Compile and run path-independent Linux programs

mwilliamson
35pts26
mike.zwobble.org 13y ago

External code quality and libification

mwilliamson
1pts0
mike.zwobble.org 13y ago

Test reuse

mwilliamson
1pts0
mike.zwobble.org 13y ago

Show HN: spur.py: A simplified interface for SSH and subprocess in Python

mwilliamson
1pts1
mike.zwobble.org 13y ago

Don't make big decisions, make big decisions irrelevant

mwilliamson
3pts0
mike.zwobble.org 13y ago

Peaks and troughs in software development

mwilliamson
1pts0
www.simple-talk.com 13y ago

Red Gate Coder interviews: Alex Davies

mwilliamson
2pts0
www.simple-talk.com 13y ago

Red Gate Coder interviews: Jeff Foster

mwilliamson
1pts0
www.simple-talk.com 13y ago

Red Gate Coders: Peter Cowan

mwilliamson
1pts0
Portable EPUBs 2 years ago

I had a similar problem loading the page on Firefox for desktop with private browsing. It turns out service workers don't work in private browsing, which it seems Bene (the software rendering the page) requires. Switching to a normal Firefox window solved the problem.

I don't know about other distros, but Debian makes it extremely easy to download both the binary package and the source package. For instance, on the page for the jq package [1], you can download the source using the links down the right-hand side, which includes the full test suite. The key, in my view, is that Debian has a nice way to associate both the final output artefact and the source (both the original source and their patches) with a specific version.

[1] https://packages.debian.org/bookworm/jq

For packages where I don't include tests, I've had at least one downstream distro maintainer request that I include tests, since at least some of them treat npm or PyPI or whatever as the source of releases.

For packages where I do include tests, I've had at least one user request that I remove tests so that the footprint of the Docker image they're building is smaller.

Both are entirely reasonable requests, but package repositories don't really provide a good way of accommodating both at the same time, for instance, by allowing a separate upload of the dev gubbins such as tests.

Reminds me of the four types of documentation that sometimes get listed: tutorials, how-to guides, technical reference and explanation. (Usual caveat of all models are wrong but some are useful.) https://documentation.divio.com/

My (perhaps overly simplistic) take would be that we should take the thinking we use on the product itself (Who's going to use it? In what context? What would they already know? And so on), and apply and adapt it to the docs as we would any other product.

To expand on listening to your gut instinct: I find taking a "trust but verify" approach useful. Take the time to dig into what your instinct is telling you, try to match it up in words to your hiring criteria (which should go beyond technical stuff, including capturing whether or not you'd want to work with the person), and compare against other candidates to check you're being consistent. For instance, you don't want to unfairly penalise someone for being loquacious just because their interview was right before lunch and you were getting hungry, whereas you enjoyed interviewing the similarly talkative candidate that happened to be interviewed just after lunch.

I GM an online TTRPG, and I wanted to replicate the experience of the players drawing the map themselves as they go along. We use Roll20, but didn't find the tools particularly well suited to updating the map in the moment.

So, I had a go at making a little tool that lets you quickly make rough sketches of the map, as well letting you move tokens (for the characters) around. It's not particularly fancy, but it seems to work for us!

https://github.com/mwilliamson/ttrpg-map-sketcher

Two thoughts:

1. I think it's worth considering how much the Baumol effect is responsible for the price changes described. Specifically: we'd expect those industries that don't benefit from improved productivity (for instance, because AI doesn't actually meaningfully help with most parts of the work) to experience price rises, since they're competing in the labour market with other sectors that have have benefited from improved productivity, and can therefore pay workers more.

Or, to put it another way, you'd expect this effect to some degree in a market operating entirely without restriction.

https://en.wikipedia.org/wiki/Baumol_effect

2. If I remember correctly, in the past, when technological innovation eliminated jobs, the new ones that sprang up generally meant a rise in wages across the board. In other words, the overall effect was low-paid jobs being replaced with higher-paid jobs.

No more. In recent years, the graph of wages of replacement jobs looks more like a U-shape: they tend to either be low-paid or high-paid. For instance, take translating a textbook. A company might decide to fire their moderately well-paid translators with a steady job, and replace them with AI (built by another company, using a smaller number (per book) of high-paid tech workers), plus some low-paid, proof readers to fix up the mistakes. And the latter group is probably precariously employed: the better the AI gets, the less they're needed. And they're probably seen as lower skilled and therefore easier to replace by the company, so perhaps their jobs look more like precarious gig economy jobs.

That's a simplification, but I think the evidence suggests that that U-shape is a pattern that, very broadly, holds true. So, we might not be unemployed, but we might have a lot of people with falling incomes and much lower job security.

(Apologies, writing this in a hurry, so can't find a source right now!)

An app for quickly and collaboratively drawing maps for tabletop RPGs.

I run a tabletop RPG for some friends over the Internet using Roll20. As a player in other (in-person) games, there have been times where we've collaboratively made a map as we've gone along rather than the GM providing one, and I wanted to be able to provide a similar experience for my players. Since we found Roll20 didn't really work for this use case, I'm cobbling together an app that tries to make the experience as fluid as possible. It's only really intended for my group when I'll be on hand to explain how it works and I'll be the only one deploying it, so the docs are somewhat sparse, but in case anyone is interested:

https://github.com/mwilliamson/ttrpg-map-sketcher

I've also been working on a compiler for the most boring programming language in the world: https://github.com/mwilliamson/clunk

I maintain a library with ports to multiple languages (JavaScript, Python, Java). They have very similar structure, which means doing the same thing in pretty much the same way three times each time I make a change.

The idea I wanted to test with my language is: is it possible to extract a common subset that compiles into reasonably idiomatic code for those target languages? The compiled interfaces should be sensible (i.e. use of the code from the target language should be as good as if written in the target language directly), while implementations can be a little less tidy, but ultimately still readable and easily refactorable if the user ever decides to eject from my language and write everything in the target language(s) instead.

I doubt I'll ever use it in anger, and since it's nowhere near ready for use of any kind there aren't really any docs. In the unlikely event someone is interested, the most illuminating thing to look at would be the very beginnings of the reimplementation of the aforementioned library. Since I use snapshot testing with examples, you can see the source code, generated code and result of running the compiled test suite in one file:

Java: https://github.com/mwilliamson/clunk/blob/main/snapshots/%5B... Python: https://github.com/mwilliamson/clunk/blob/main/snapshots/%5B... TypeScript: https://github.com/mwilliamson/clunk/blob/main/snapshots/%5B...

I'm not sure how well it would work (if at all!), but an alternative could be for a specific prediction to become less valuable as it becomes more popular. In other words, reward players not for absolute accuracy, but for making bets on underpriced outcomes.

Location: Cambridge, UK

Remote: Yes, or hybrid/office in Cambridge

Willing to relocate: No

Technologies: Most recent experience is with Python, TypeScript, React, GraphQL and PostgreSQL, but I've also worked with C# and Java. Happy to learn new things too!

CV: Available on request

Email: whoishiring@michaelwilliamson.co.uk

I've worked for the last 12 years as a full stack web developer. That includes building backends, frontends, APIs, data ingestion tools, infrastructure in AWS.

I've worked in various domains, such as education and biotech. I'd like to contribute to something that improves the world in some way. That might be the obvious stuff like helping students learn or finding treatments for cancer, or it might be something less glamorous like working on database technologies. It probably isn't working on blockchain or in fintech.

I enjoy working in cross functional teams: that is, I like teams that have all the skills needed to consistently deliver business value, whether that's typical software roles like developers and designers, or something more domain-specific such as pharmacologists.

If you're just looking for someone to pick up tickets and churn out code, I'm probably not a good fit. I'm interested in the wider team and company goals and strategy, and would like to work in a team where everyone can contribute to product thinking, rather than (for instance) having a product manager that writes all of the tickets. In the past, I've built internal tools and worked with the domain experts that use the tools to work out what problem to solve next, and what the next most valuable increment to build would be.

I'm also interested in how people and teams collaborate. For instance, in my experience, meetings are often unstructured chatting, or have one or a small number do the vast majority of the talking. This means that many people don't meaningfully contribute -- frustrating for them, and a loss to the group as a whole -- and decisions aren't made, aren't clear, aren't understood, or aren't widely supported. I've successfully used techniques such as those in the Gamestorming book and Liberating Structures to add structure that allows everyone to contribute, for a range of options to be discussed and considered, while reaching a clear decision at the end.

I've tended to work in smaller organisations since much of the above tends to happen naturally in those circumstances, but I'd be open to working anywhere that's a good fit and treats its people as more than fungible units of resource production.

I've also gotten a Framework laptop, and am happily running XFCE on Debian testing.

One thing I'm curious about is how other people are handling the resolution of the screen on Linux? It doesn't seem like XFCE currently supports fractional scaling well, so I ended up defining a custom 3:2 resolution and using that to increase the size of everything by about 1.5x.

Author here! The open sourced code was rewritten from scratch, not least because the original version was in C# while the open source version is in JavaScript. So, the same idea, but an entirely new implementation.

I forget exactly what conversations I had, but it was also very clear that they had no problem with me doing so since it doesn't really have any connection with their core business. If I wanted to, I suspect I could have open sourced the original version so long as I stripped out the stuff that was specific to the company.

Yup, precisely should work fine with mutable / unhashable types -- it just relies on equality to check the elements in an iterable.

More specifically, writing:

    contains_exactly("a", "b")
is equivalent to:
    contains_exactly(equal_to("a"), equal_to("b"))

(I'm the author of precisely)

If you were to ask me "Should I use precisely in my tests?", my answer would be: it depends. The main benefit is to better describe the intent of your test, so that the assertion is neither under- nor over-specified, with the intent directly stated.

Assuming your assertion is meant to be an alternative to the example in the README:

    assert_that(result, contains_exactly("a", "b"))
I'd suggest that the above states the intention of the assertion, rather than how you check it. For instance, your assertion would allow duplicate elements, whereas the assertion as originally written would suggest that this isn't desired. As other comments have pointed out, you can do things with sorted, Counter or set (depending on exactly what you want to assert), but why worry about what trick to use when you could just directly state your intention?

The assertion using precisely is also (arguably) easier for a reader to know what is (and isn't) being asserted in the test, and makes the test less brittle since you're not accidentally asserting more than intended (for instance, it's common to assert equality with a list, even though you don't actually care about order).

Another common case is when you want to make assertions on a collection, but equality would check too much. For instance, suppose you have a function that fetches users from a database. The fetch can return the users in any order, and you just want to check the names of the returning users, so you can write something like:

    assert_that(result, contains_exactly(
        has_attr(name="Alice"),
        has_attr(name="Bob"),
    ))
How would you write something that means the same thing without precisely? The order isn't deterministic, so you can't write something like:
    assert result[0].name == "Alice"
    assert result[1].name == "Bob
    assert len(result) == 2
We could sort the users by name before making the assertion:
    result = sorted(result, key=lambda user: user.name)
    assert result[0].name == "Alice"
    assert result[1].name == "Bob
    assert len(result) == 2
Personally, I prefer the precisely assertion!

What about something like an equality assertion?

    assert set(result) == {
        User(id=1, name="Alice", email_address="alice@example.com"),
        User(id=2, name="Bob, email_address="bob@example.com"),
    }
Now we've over-specified our test -- we need to know irrelevant details like the ID and e-mail address of the users, which might change and break this test even when the functionality we care about still works. We'll also break the test if we add any more attributes to users.

As you've mentioned, there's a cost to learning precisely. Even if you're familiar with the library, then you're still potentially writing a more complex assertion (where more can go wrong) than just (for instance) an equality assertion. In my experience, on many projects, the ability to state the intent of assertions precisely has far outweighed the downsides, but that's from a position of already being comfortable with the library.

(I'm the author of precisely)

Better error messages was the motivation: PyHamcrest tends to put everything onto one line, which makes it hard to discern structure. In contrast, precisely tries to produce an error message that uses line breaks and indentation to match the structure of the matcher, hopefully making it clear exactly which part of the matcher has failed.

Similarly, I've found one of the most useful questions to pose for each option is "What would need to be true for this to be a good idea?". This is helpful whether you're initially supportive of or opposed to the idea. In either case, you have to be explicit about the conditions/facts you think are necessary for success and why they're either true or false, which tends to lead to a more concrete discussion with more space for agreement and shared understanding. If you're supportive, this makes it easier to acknowledge that those conditions/facts might not necessarily be true. If you're opposed, this makes it easier to appreciate the idea and work on it despite your reservations.

The cynic in me says it's because many of them are cheap generics that have little financial upside for large pharmaceuticals.

Social impact bonds are an interesting idea that might help solve this, although I know little about the practicalities. Specifically, a company would pay for the research and trials, and if it's effective, the government (say, the NHS in the UK) would pay a portion of the money saved to the company.

The solution we use at work is to put all of the GraphQL strings in separate `.graphql.ts` files. A script then scans the code base for such files, requires them, and converts queries/fragments into TypeScript types in a corresponding `.graphql.types.ts` file using apollo-codegen.

The advantage of this over plain .graphql files, or statically extracting queries, is that you can use all of the normal tools of TypeScript for composing queries (mainly imports and template literals), and you don't need to have globally unique names for all your queries and fragments. Having said that, I haven't looked at the recommended Apollo workflow in a little while, so I'm not sure if those issues still exist.

Rapid feedback and short iteration cycles are often considered to be an important part of agile development. TDD suggests that you write a test first, and try to make that test the smallest increment that moves you forward. It feels to me that TDD is an application of the principles of rapid feedback and short iteration cycles on the level of code.

If you think using TDD improves the quality of your code, then it's also important for maintaining high code quality, since that enables some of the other agile practices, such as frequent releases.

I don't worry too much about whether a particular practice is agile or not. If you find TDD useful, then great! It's often included in conversations about agile development, but that doesn't mean you have to be doing agile development in order to use it.

I think the value I get out of Scrum, or XP, or any other methodology, is two-fold.

Firstly, it gives you a set of tools to use -- sprints, TDD, product owners, and so on -- that should work well together. To use them well, I think you both need an understanding of how each of those tools supports some idea or principle that the methodology promotes, and whether or not you think that principle is important for your team.

Secondly, a methodology gives you a starting point of how to run a development team. It's easier, although not necessarily better, to start from a pre-existing cohesive set of practices than to build a process from scratch. Especially if a team isn't experienced with many of the ideas, this is not an unreasonable place to start.

I think the key is:

1) knowing what principles your team thinks is important (low defect rate? frequency of releases? developer happiness? empowerment?). This is often hard to articulate. As time goes by, you might discover principles that were previously implicit, or you might find that the principles that are important to you change.

2) knowing how each part of your process maps back to one or more of those principles

3) having a mechanism that allows you to tweak your process over time (for instance, retrospectives), whether that's adding, changing or removing parts of your process

Scrum can be a sensible starting point so long as you're willing to introspect and consider which bits are and aren't working for you, and you're empowered to do something about it.

Healx | Full stack developer | Cambridge, UK | ONSITE | https://healx.io/

Healx is a Cambridge, UK startup using computational methods to identify existing drugs that may treat rare diseases. We have data coming out of our ears, including biological data, scientific literature and the results of our analyses, so we've built internal web tools to help our team to make sense of it all. We're hiring a full stack developer to help us find better ways of presenting the data, whether it's for pharmacologists exploring the evidence that a drug may treat a disease, or salespeople finding information about a disorder and related charities.

To apply, please email jobs@healx.io with your CV and a few words explaining why you're interested in the role.

I wrote a little proof of concept in JavaScript showing one way of dealing with the problem [1]. Essentially, as you go through each node in the request, build up a query (say, a SQL query) that represents the fetching of that node by joining it with the query of its parent. Then, stitch the result of running each query back onto the result of running the parent query. Conceptually, this means one resolution function call for each node in the request, rather than one resolution function call for each node node in the response.

[1] https://github.com/mwilliamson/node-graphjoiner

However, it's rather verbose with repeated boilerplate, so to make the approach work I think you'd want to put a higher-level layer on top -- that's what I've done with a Python implementation [2]. I think the general approach is the valuable part, rather than the specific libraries, and seems more in keeping with how I'd normally write queries than the usual DataLoader approach. In particular, I've found it tends to improve performance when fetching large data sets since you don't need to call a resolution function on every single field that gets returned. (DataLoader implementations batch queries together to avoid the n+1 problem, but they mean that you need to implement a batching layer, and probably make your resolution functions asynchronous, which is especially expensive for large data sets).

[2] https://github.com/healx/python-graphjoiner

To me, the most interesting example of a similar organisation (that I see rarely mentioned) is AES (an energy supplier) under Dennis Bakke, as described in Joy at Work [1]. As he puts it, every decision made at the top was a lost chance to delegate responsibility. He advocates the advice process: any person can make any decision, provided that they first get advice from anybody with expertise, and anybody who will be affected. Note that consensus is not required: one of the desired results is that, freed from the need to persuade others, you can focus solely on listening and understanding other points of view. You're hopefully better informed to make a decision, and others feel listened to (rather than feeling like their points aren't being heard as you're trying to persuade them).

Not only did AES have tens of thousands of employees, many of them joined by way of acquisitions of existing plants, rather than being hired by AES with their ethos in mind. That's not to say that everybody was a good fit (some people chose to leave given the choice), but that many stayed and eventually appreciated the change in management structure challenges the idea that self-management is only appropriate for a small proportion of people.

[1] http://www.dennisbakke.com/joy-at-work

Made to Stick, by Chip and Dan Heath [1]. They ask "how is it that certain ideas seem to stick our minds better than others?", and give concrete advice on how to improve the stickiness of your own ideas. I've found it useful to avoid forgettable business waffle that fails to change people's minds nor behaviour. One of the many examples they give is of Nordstrom (a fashion retailer). They could have said "we want to delight our customers". Instead, they use stories of employees that embodied those principles: ironing a shirt for a customer that needed it that afternoon, refunding tyre chains even though Nordstrom doesn't sell tyre chains.

[1] http://www.amazon.co.uk/Made-Stick-ideas-others-unstuck-x/dp...

Static Linux 12 years ago

And that feature disparity includes security updates. If a library is updated with a security fix, you'll need to update everything that uses that library to get the fix, rather than just the shared dynamic library.