Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156398 stories
·
33 followers

Reinventing The Wheel, Now At A Bargain Price!

1 Share

For twenty years, saying “don’t reinvent the wheel” was usually enough to win any argument about writing bespoke software. But that advice assumed two things: writing code was expensive, and importing code was cheap. AI and supply-chain governance are changing both assumptions.


A Short Personal History of Libraries

When I started in software it felt like the wild west in terms of security, audit and compliance. I worked in an unregulated industry as a third party supplier to some FTSE-100 businesses as an engineer. I could tell stories about access and identity management (or the lack of it) that would turn a 2026 CISO’s hair white (or, more likely, whiter).

Wind forward a quarter of a century, and now security is not only its own discipline, but (mostly) fully baked into the software development lifecycle. Compliance is on its way to the party too (this is the area I’m working on at the moment, contact me etc.).

In parallel, software libraries have also become more and more embedded into the software development lifecycle. It may be hard for younger readers to believe, but I spent many years maintaining incredibly busy software platforms that rarely added a publicly available library to its codebase. If we did add them, they were mostly small enough that we could (and usually did) inspect the code ourselves.

These days, it’s not a software project unless you have thousands of npm dependencies you have never even heard of downloaded on an npm install, and my go.mod files can be even longer than the source code for some of my smaller or half-started projects.


The Software Library Pact

Outsourcing engineering to third party libraries always was a trade-off. Writing software was expensive and dangerous. It was far better to outsource bits of that to a freely available online solution. This resulted in a Faustian pact: add all these barely understood dependencies and you won’t need to find the money to finish your project. Aside from the financial benefit, you would get free upgrades, extra functionality you might need in the future, and so on.

This pact was popularised with the slogan ‘do not re-invent the wheel’, which is ancient wisdom to guard against the well-known tendency for engineers to build things unnecessarily to satisfy their creative urges.

However, the consequences of that (perfectly rational) pact are catching up with the industry. Mephistopheles has arrived to take his due, and now engineers’ dependency additions are now continually scanned and analysed for weaknesses by black hat crackers, white hat hackers, supply chain crypto thieves, and auditors alike. When a widely used library has a flaw discovered in it, millions of voices suddenly cry out in terror and are immediately busy managing patches.

It was while talking about these challenges of onerous library management recently with a friend that a thought clarified for both of us that had been circling our consciousness since the advent of LLMs:

Is it now sometimes safer and cheaper to write and maintain bespoke code, rather than importing libraries?

I know this sounds crazy, but let me explain. My friend works in the defence industry. Every new library involves an immense amount of bureaucracy to justify its addition, map its dependencies etc.. Then, once the library is embedded in the codebase, chances are high that flaws will be found in it (or one of its dependencies) that will incur significant bureaucratic costs to upgrade. Think of it like a dependabot workflow, but each alert is a punch in the face.

By contrast, application code that is peculiar to the code requires much less bureaucracy to implement and maintain.

Given that application-specific code is now very significantly cheaper to produce than it has been in the past, and using libraries is now more expensive than it has been in the past, then the calculus behind software delivery has been radically altered. Is it now sometimes safer and cheaper to write and maintain bespoke code, rather than importing libraries? The specific example my friend adduced was a large open source library, a small subset of which’s functionality he wanted to use. He ended up reimplementing that small piece with an LLM, as he reasoned that in that case the various trade-offs were worth it.

I mentioned this notion to an experienced CISO recently and it’s fair to say he was not amenable to the idea. I won’t retail his exact words, as I know some readers have delicate sensibilities.

The CISO objection is not stupid. The industry has learned the hard way that bespoke code is often unreviewed, under-tested, undocumented, and full of boring vulnerabilities. “We wrote it ourselves” is not a security argument.

I’m not giving up on the argument that the calculus has changed though, because I think there’s an economic case to be made in an increasing number of cases. The old mental model was that “npm install is free!”, whereas the new model is that every dependency is a recurring obligation. Each library creates future work: CVE triage, version bumps, breaking changes, SBOM entries, licence checks, audit queries, exception handling, patch windows, and operational risk.


Benefits

Let’s itemize the benefits of choosing to build your own functionality than import dependencies:

1) Writing Code Is Cheaper Than It Was

LLMs can spew out functionality all day, and they’re far, far better at that than maintaining and reasoning about larger systems. You still have to make sure the code it produces works as intended, write tests (LLMs can help with that too), etc., but it’s undeniably cheaper to write an internal library to (for example) prepend characters to a string than it was pre-LLM.

2) It’s Private

Your internal library does not get picked up by code scanners (unless they can comb through your source code directly, in which case they are welcome to find fault). Your internal library does not need to be put in an SBOM. This will reduce noise right across your SDLC.

In other words, bespoke code removes one class of externally visible dependency risk: maintainer compromise, dependency confusion, abandoned packages, and surprise transitive upgrades. It does not remove the need to scan, test, review, and threat-model the code itself.

3) It’s Crafted for Purpose

Code that you make for your needs does exactly what you need it to, and no more.

It’s not subject to any particular library’s bloated attack surface now, or in the future. It doesn’t pick up an increasing number of secondary dependencies over time as it tries to cover more and more features and corner cases you don’t have.

4) It’s Cheaper to Maintain / More Secure

We’ve already discussed some of the ways it can be easier to maintain, but it’s worth itemising the kinds of attacks and disruption that we’ve seen due to third party dependencies:

  • left-pad (2016), where the withdrawal of 11 lines of non-obfuscated code introduced as a library dependency resulted in widespread panic
  • event-stream (2018), where another widely-used package was used as part of a supply chain attack that tried to steal Bitcoin wallets
  • faker.js (2022), where the maintainer decided to ‘upgrade’ his code to a cris de coeur about the ingratitude of the industry towards open source maintainers

The failure modes are not “the library has a bug.” They are: the maintainer disappears, the maintainer is compromised, the maintainer turns hostile, a transitive dependency changes, a package is unpublished, or the ecosystem shifts under you.

A recent discussion about vulnerability management on HN has some good illustrations of the tensions.

I could also bring in arguments about security through obscurity being a useful (but obviously not sufficient) line of defence, but the itself phrase seems to trigger people, so I won’t.


Objections

The question is not whether we should stop using libraries. The question is whether some dependency imports are now harder to justify than bespoke, narrowly scoped code.

But since what I’m talking about is a trade-off there are of course reasonable objections to raise. I’ll itemise them here, and argue how these arguments’ force has changed recently.

1) It Will Be Less Secure

Your code is specific to you, and hasn’t been subject to a ‘many eyes make bugs shallow’ process of open source delivery. Your code might have old-fashioned flaws like unchecked input, for example.

These objections are self-evidently true. However:

  • LLMs (and other modern tooling) can perform a lot of the security checking of code locally, and far more cheaply than a traditional sec team.
  • Libraries (as we’ve seen above) have attack vectors that make them less secure also.

2) It Will Be Harder to Maintain

I don’t think that will always be true now that we have some form of intelligence to scour our code. Further evidence for how it is hard to maintain libraries is outlined above.

If your code talks to a well-known API for example), then you could argue that using a public library will make you more future proof. Whether that argument holds will depend on how often and significantly the API changes, how much of the API you need to interact with, and how likely it is that the library will be well-maintained in future. I’ve spent a fair amount of time in the last 10 years chasing down poorly maintained libraries that haven’t kept pace with their target API, resulting in me spending significant effort replacing one library with another (or even, not infrequently, having to patch the library with some code I write myself as no such library now exists).

3) If Your Engineers (Now Or In The Future) Are Clueless, This Could Be A Disaster for You

Absolutely.

4) Some Libraries Are Too Important To Rewrite Yourself

Yup. I definitely don’t recommend rolling your own cryptographic libraries, or implementing your own timezone management functionality.

LLMs do not make writing hard software easy. It makes some boring ‘already solved’ software cheap enough that importing a dependency is no longer obviously the cheapest option.


Towards a Decision Framework

Here I propose a quick checklist of things to consider when deciding which way to go. You might want to score each one and to see which way you want to go.

Points for the library:

  • The domain is security-sensitive, complicated or difficult to correctly code
  • The library is mature and widely reviewed
  • The library is well-supported and likely to be maintained for a long time
  • The library has few transitive dependencies (and those dependencies are themselves solid projects)

Consider writing your own when:

  • You only need a small subset of a large library
  • The libraries’ used functions are simple and easily specified
  • The dependency brings many transitive dependencies
  • Approval and vulnerability-management costs are high
  • The code can be exhaustively tested by code you can maintain
  • The blast radius is low

If you feel this was useful, consider buying me a coffee to say thanks!

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Accelerating Transformers Fine-Tuning with NVIDIA NeMo AutoModel

1 Share
Read the whole story
alvinashcraft
13 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Context Windows Are Not Memory: What AI Agent Developers Need to Understand

1 Share
In this article, you will learn why a large context window is not the same thing as agent memory, and how techniques like retrieval, compression,...

Read the whole story
alvinashcraft
21 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Building Browser-Using AI Agents in Python

1 Share
Most AI agent tutorials start with an API.

Read the whole story
alvinashcraft
30 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Building effective human-agent teams

1 Share
Building effective human-agent teams
Read the whole story
alvinashcraft
36 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Windows Package Manager 1.29.280

1 Share

This is a release candidate of Windows Package Manager v1.29. If you find any bugs or problems, please help us out by filing an issue.

New in v1.29

New Feature: Source Priority

Note

Experimental under sourcePriority; defaulted to disabled.

With this feature, one can assign a numerical priority to sources when added or later through the source edit
command. Sources with higher priority are sorted first in the list of sources, which results in them getting put first
in the results if other things are equal.

Tip

Search result ordering in winget is currently based on these values in this order:

  1. Match quality (how well a valid field matches the search request)
  2. Match field (which field was matched against the search request)
  3. Source order (was always relevant, but with priority you can more easily affect this)

Beyond the ability to slightly affect the result ordering, commands that primarily target available packages
(largely install) will now prefer to use a single result from a source with higher priority rather than prompting for
disambiguation from the user. Said another way, if multiple sources return results but only one of those sources has
the highest priority value (and it returned only one result) then that package will be used rather than giving a
"multiple packages were found" error. This has been applied to both winget CLI and PowerShell module commands.

REST result match criteria update

Along with the source priority change, the results from REST sources (like msstore) now attempt to correctly set the
match criteria that factor into the result ordering. This will prevent them from being sorted to the top automatically.

Minor Features

Preserve installer arguments across export and import

winget export now captures the --override and --custom arguments that were used when a package was originally installed and saves them into the export file. When subsequently running winget import, those values are automatically re-applied during installation — --override replaces all installer arguments and --custom appends extra switches — so packages can be reinstalled with the same customizations without any manual intervention. Both fields are optional and independent of each other; packages without stored installer arguments are unaffected.

--no-progress flag

Added a new --no-progress command-line flag that disables all progress reporting (progress bars and spinners). This flag is universally available on all commands and takes precedence over the visual.progressBar setting. Useful for automation scenarios or when running WinGet in environments where progress output is undesirable.

MCP upgrade support

The WinGet MCP server's existing tools have been extended with new parameters to support upgrade scenarios:

  • find-winget-packages now accepts an upgradeable parameter (default: false). When set to true, it lists only installed packages that have available upgrades — equivalent to winget upgrade. The query parameter becomes optional in this mode, allowing it to filter results or be omitted to list all upgradeable packages. AI agents can use this to answer requests like "What apps can I update with WinGet?"
  • install-winget-package now accepts an upgradeOnly parameter (default: false). When set to true, it only upgrades an already-installed package and returns a clear error if the package is not installed (pointing to install-winget-package without upgradeOnly instead). AI agents can use this to answer requests like "Update WinGetCreate" or, in combination with find-winget-packages with upgradeable=true, "Update all my apps."

Authenticated GitHub API requests in PowerShell module

The PowerShell module now automatically uses GH_TOKEN or GITHUB_TOKEN environment variables to authenticate GitHub API requests. This significantly increases the GitHub API rate limit, preventing failures in CI/CD pipelines. Use -Verbose to see which token is being used.

Default priority of installer types

Installer type selection no longer depends on the order defined on the manifest. Instead, preference is given in this order:

  • MSIX
  • MSI / Wix / Burn
  • Nullsoft / Inno / EXE
  • Portable

When a user configures installer type requirements or preferences, the order in which they are listed is now respected during installer selection.

Improved list output when redirected

  • winget list (and similar table commands) no longer truncates output when stdout is redirected to a file or variable — column widths are now computed from the full result set.
  • Spinner and progress bar output are suppressed when no console is attached, keeping redirected output clean.

Log file naming strategy

Added a user setting (logging.fileNameStrategy) for controlling the default naming strategy for installer log files. Supported values are manifest (default), timestamp, guid, and shortguid. Only applies to logs generated by installers if the installer itself supports the logging switch / parameter.

Setting Description
manifest Uses the name of the manifest and a timestamp. Has the same behavior as WinGet 1.28
timestamp The log name is just a timestamp
guid The log name is a GUID
shortguid The log name is the first 8 characters of a GUID

Sortable list output

winget list now supports sorting results via --sort <field> (repeatable for multi-field sorting), --ascending/--descending direction flags, and a persistent output.sortOrder setting. Available sort fields: name, id, version, source, available, relevance. By default, results are sorted alphabetically by name when no query is present; use --sort relevance to preserve the previous source-determined ordering.

Bug Fixes

  • winget export now works when the destination path is a hidden file
  • Fixed the useLatest property in the DSC v3 Microsoft.WinGet/Package resource schema to emit a boolean default (false) instead of the incorrect string "false".
  • SignFile in WinGetSourceCreator now supports an optional RFC 3161 timestamp server via the new TimestampServer property on the Signature model. When set, signtool.exe is called with /tr <url> /td sha256, embedding a countersignature timestamp so that signed packages remain valid after the signing certificate expires.
  • File and directory paths passed to signtool.exe and makeappx.exe are now quoted, fixing failures when paths contain spaces.
  • DSC export now correctly exports WinGet Admin Settings
  • winget validate now performs case-insensitive comparison for file extensions where applicable
  • winget source reset now properly resets default sources instead of removing them
  • DSC v3 Microsoft.WinGet/Package resource now honors the installMode property to use silent or interactive installer switches as specified
  • Fixed a crash (0x8000ffff) when using --disable-interactivity with the Resume experimental feature enabled during install operations.

What's Changed

  • [1.29] Fix crash with --disable-interactivity and EFResume by @Trenly in #6303
  • Fix configuration elevation validation for standard flow (1.29) by @JohnMcPMS in #6318

Full Changelog: v1.29.250...v1.29.280

Read the whole story
alvinashcraft
56 seconds ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories