When I was a student, and using a shareware or trial version of some software and wanted some printed output from it without a watermark, I printed to postscript (chose a printer that supported postscript and the driver used it instead of rasterized images), but using a file instead of a printer.
I could then open up the postscript, delete the commands that rendered the watermark, save it, then I converted it to PDF so it would be easy to print.
You don't need PostScript for that. The PDF text commands are Tj and TJ, and rarely ' and ". They are easy to delete without going through PostScript. Tj means showing a simple text string. TJ means showing an array of strings possibly with space adjustments. ' means moving to the next line and showing a simple string. " means doing that and setting character spacing.
Perhaps, but it's easier to open up and edit a .ps file in a text editor than a PDF. PDF is a binary format with compressed streams, while postscript is just a stack-oriented programming language.
Tools like qpdf makes it easy to edit a .pdf file in a text editor too. I’d argue using such tools is easier than and simpler than printing to postscript.
> real and only difference between a library and a framework, is whether it introduces new concepts
This isn't what is normally understood in software engineering by those terms.
A library is something you call.
A framework is some kind of application scaffolding that normally calls you.
You can use more than one library. You normally only have one framework in-process.
I found the blog post a little hard to parse. Is it an argument against wrapping frameworks, or wrapping libraries?
I agree that wrapping frameworks is fraught with danger. I can't quite agree for wrapping libraries. Wrapping libraries makes a lot of sense if you're only using a tiny fraction of the library functionality, the breadth of the wrapper's API is much smaller than the library's API, wrapping it enables you to substitute it (whether for a smaller / faster / whatever dependency in the future, or for testing, etc.), and so on.
If you look above the unorthodox library/framework distinction, I think the criticism is about birthing new (inadvertently leaky) abstraction layers with new semantics to capture the specifics of the domain. Often with either esoteric words attached to supposedly novel patterns, and/or unconventional usage of existing terminology.
The promise is to simplify and unify things but as noted, such efforts often have the opposite effects.
"Teams are struggling with properly adopting FooTech - our FooBarTool wraps it in a beautiful package and magically solves everything with a single command and one config file"
This is the real problem. Too often frameworks/libraries are geared towards making things magic instead of making things solid. Magic solutions are usually very one dimensional. e.g. The Magic only works for a really narrow use case, or at low load. I don’t think this is specifically a problem with “mini frameworks” but homegrown stuff exhibits this more, if only because magic solutions tend to die in the wild when the bug tracker is full of “this only works for trivial case; make it actually work”.
When frameworks/libraries advertise how easy they are to get started, there is often a lot of magic to make it trivial to start and they don’t scale to real projects without breaking through all the magic abstractions.
That's one thing I'll say for in-house "mini-frameworks": Real frameworks often try to present a smooth façade of abstraction which is hard to punch holes through. An in-house "mini-framework," though, is often just a hodgepodge of compostable abstractions which you can individually take or leave without too much hassle.
Of course, your hodgepodge ecosystem will eventually ends up a bit of a mess, but I'll take that over a flashy off-the-shelf framework that insists on handling everything itself.
I have never really grokked Ruby on Rails and I passionately hate all the frameworks that try to adapt it to some other language.
That said, I suspect that Ruby on Rails itself occupies kind of a special space where the magic is acceptable because people who write Ruby are used to having very very sharp tools and have learned to wield them carefully. Give that magic to a PHP or Java programmer and there is immediately gallons of blood on the floor.
(says former Rubyist who was put off by the RoR stuff because I'm apparently more of a Haskeller at heart.)
Yeah had so many discussion with senior developers in my life to argue for just keeping things simple, but my god they love abstractions.
They are clearly always very smart and understand the code base well.
Maybe it’s their intelligence wanting to be more utilised or maybe they are bored and trying to over engineer simple problems
I would narrow this down further. Programmers (myself guilty) live abstractions they control. It gives them the ability to tweak little things and feel like they've done it in a more maintainable way.
Programmers HATE using other people's abstractions, which is why "mini frameworks" tend to tall apart after expanding to teams that don't have control over it. In my experience, this leads to new mini frameworks wrapping the first one, forever targeting a static version of the underlying MF, to allow for adding new appendages without going through a gatekeeper.
It is easier to write your own code than to understand someone’s else code.
It would be much better if we get more humble people who get down to work to understand why and how existing code or framework works instead of trying to work around it or simply throwing all away and writing their own code.
Thera are of course some exceptions - but those are like pro athletes - no you are not the one, learn existing code instead of making excuses about some edge cases you ran into so you have to rewrite all from scratch.
It's a mixed bag.. If you spend much time in a poorly reviewed ecosystem then you are quickly taught that if you try to make things work in their broken crap you will lose a lot of time and have no code to demonstrate you were working at all.
If you always inject the same repair layer approach of your own crap then you ship more reliably and have your pleased client and your lock-in. The pleased client then has to decide if they should listen to a replacement who says you are insane but can't seem to ship anything.
This view needs to be balanced. There's a lot of cases where the previous code didn't abstract the domain well, or the domain has changed (mostly at the application level). Fighting the ill-fitted abstraction is a real chore in this case and quickly lead to complex code.
I'm all for aggressive refactoring if it leads to something simpler.
It sounds like you are agreeing - that digging into the existing layers can be a good idea that is often neglected or resisted to a fault. That we should treat existing software less like black boxes.
There are caveats here too of course - forking every single thing and excessive NIH syndrome comes with their own costs and risks.
I don't mind layers of abstraction when they work well and their components compose nicely. Like a well-designed programming language. These can actually be quite fun to work with.
Layers of abstraction where the boundaries between that layer and those around it are fuzzy to non-existent and where certain cases magically work and everything else is a janky mess because it was never designed to work are what give me headaches and want to throw my work laptop out the window on a regular basis.
I concede I had a bit of second thoughts after posting the comment you replied to.
After all, hindsight is 20/20 and at the end of the day a major part of our work is making (appropriate) abstractions. Picking good terminology is sometimes hard. I guess to point out that the message shouldn't be read as "all abstractions bad (stop trying)" but to be more conscious about them and to restrain from piling new "magic" on top of a tower of old apocrypha.
> A library is something you call.
> A framework is some kind of application scaffolding that normally calls you.
I think I broadly agree with this. In essence, libraries don't impose an application-level life cycle. Frameworks, generally, do.
The rest of the article, I don't know….
The most successful, long-running app I maintain has a mini-framework that allowed me to assemble what I need piecemeal rather than relying on any off-the-shelf framework that would have been obsoleted several times over in the seventeen-year lifespan of this code.
I guess about one in three things I do in it require me to dip into my framework code to look at it, but this is mostly to remember how it works! About one in five things have required me to make a small progressive change.
Twice in its lifetime a core component has been swapped out (mailer and database library).
And twice in its lifetime, because it is beginning to converge on a general web framework, I have considered porting the code out of it and into a general framework, which might make it easier to hand over. One day, I suspect, something will break compatibility in a way that makes that the sensible route, but the code works, fast, has a pretty obvious set of abstractions, and there are implicit examples of everything it can do in everything it already does.
Almost all articles like this start out with "here is a thing I claim is a generalised problem that I am sure you should not do", that is a well-meaning but false generalisation, and is then caveated to the point where no new point is being made.
Underneath they are always: don't write bad code. If you do, learn from it.
If I'd followed the advice of this article when I started this project, I would by now have rewritten the entire thing more than once, for little gain.
Much more concise and much more sensible: consider whether your additional levels of abstraction have value.
But do mini frameworks have value? Sure they do, especially if there is setup and teardown that every function needs to do.
The best assumption to start with is that adapters are bad by default, because they are an unnecessary layer to maintain (and potentially a point of failure and bottleneck depending on what they are and do). Then, make the argument for the adapter as a guilty until proven innocent case.
If you can make a solid case for it, fine. There are many solid cases for adapters, e.g. drivers for a database or hardware.
Never write an adapter that you can handle more scalably, flexibly, and almost as easily by calling something directly.
A potential counter point here is that wrappers help reducing churn if the library being wrapped is actively developed - this can apply to both external libraries and ones developed by other internal teams. A wrapper limits the surface area that needs updating and can make some otherwise quite painful upgrades easier, at the cost of maintaining the wrapper itself. As ever, it’s a situational thing of course!
Wrappers are a great idea if you need a small set of what the library provides. Also very valuable if the library you are using doesn’t have good support for testing.
Certainly you should not wrap a library “just because”. The benefit of doing so should be easy to articulate.
> A framework is some kind of application scaffolding that normally calls you.
This. A framework relies extensively on inversion of control. It provides the overall software architecture of an application, and developers just inject the components called by the app to customize some aspects.
The word "concept" here is doing extreme amounts of heavy lifting here.
If I had to bring the point home I'd say that the author himself committed the very mistake he is warning against in that very sentence.
Frameworks define conventions and non-language features (language feature=data structures, callable functions, etc) that cannot be expected to be understood by simply reading the existing code and therefore need to be learned explicitly by reading the documentation of the framework. Frameworks are allowed to bend rules and expectations, because you're supposed to learn and be aware of them ahead of time.
This directly applies to the irresponsible use of the word "concept". The meaning of what he was trying to convey cannot be understood based on simply reading the sentence.
these things are hard, maybe impossible to define.
For example I mostly agree with your calls/called definition but you also get self-described libraries like React giving you defined structure and hooks that call your code.
React is 100% framework. They even bring their own DSL. It's absurd to call React library.
Library is something that can be pulled off project and replaced with something else. There's no non-trivial project where replacing React with anything would be possible. Every React web app is built around React.
100% this. To this day the official website still describe itself as a library, and I'm convinced it's completely for marketing reasons, since 'framework' feels heavy and bloated, like Angular or Java Spring, while 'library' feels fast and lightweight, putting you in control.
Framework can be more or less modular, Angular or Ember choose to be 'battery included', while React choose to be more modular, which is simply choosing the other end of the spectrum on the convenience-versus-flexibility tradeoff.
React ostensibly only care about rendering, but in a way that force you to structure your whole data flow and routing according to its rules (lifecycle events or the 'rules of hooks', avoiding mutating data structures); No matter what they say on the official website, that's 100% framework territory.
Lodash or Moment.js, those are actual bona fide libraries, and nobody ever asked whether to use Vue, Angular or Moment.js, or what version of moment-js-router they should use.
I think absurd is a bit strong. It'd be absurd to call something like rails a library.
I think you can probably see that distinction already, but to spell it out React is described as a library precisely because it does just one thing - the view - and leaves it to you to figure out the entirety of the rest of the stack / structure of your app.
Framework, at least to me, but I also believe commonly, means something that lets you build a full application end to end using it.
You can't do that with React unless your app is just something that lives in the browser either in-memory or with some localstorage backing or something. If that's your app, then probably I'd agree React is your framework per se, but that's hardly ever the case.
By the way, back to my original point, I still do think these things are impossible to define and in lots of ways these terms don't matter - if it's a framework for you, it's a framework - but I just had to defend my position since you described it as absurd :-)
React is a framework for blatantly obvious reasons.
It introduces JSX, which is technically speaking its own programming language independent of JavaScript.
It defines hooks like useState() and useContext() for state management, meaning it is not UI only, plus "function based" components that act as a pseudo DSL that is pretending to be functional JavaScript (which it isn't).
Most of the time you're expected to render the entire page body via react.
You couldn't get further away from the idea of a library.
Most people don't but you absolutely can use React a library. When React was very new, it was popular to use it as a view layer with backbone.js. In that usage, it's essentially a sophisticated templating library.
You can use Spring as a library if you really want to as well, but it's still a framework.
Id maybe concede that frameworks are a subset of libraries. You can use most frameworks in a library fashion, but the opposite is not true (you'd need a mini framework)
Agree, for modern React with hooks. A React component looks like a normal function, only you can't call it. Only React can call it, in order to set up its hooks state.
That’s because React started as a small, focused library and evolved as even more than a framework, a whole ecosystem, complete with its own best practices
React's homepage says "The library for" and "Go full-stack with a framework. React is a library. It lets you put components together, but it doesn’t prescribe how to do routing and data fetching. To build an entire app with React, we recommend a full-stack React framework like Next.js or React Router." and "React is also an architecture. Frameworks that implement it let you..."
React's Wikipedia page says "React ... is a free and open-source front-end JavaScript library", and has no mention of Framework.
Why die on a hill that it "is" something it says it isn't?
> Why die on a hill that it "is" something it says it isn't?
Because I think they're wrong about that.
If you'd prefer a different metaphor this is windmill I will tilt at.
To provide a little more of a rationale: React code calls the code I write - the JSX and the handlers and suchlike.
It's also pretty uncommon to see React used at the same time as other non-React libraries that handle UI stuff.
Most importantly, the culture and ecosystem of React is one of a framework. You chose React at the start of a project and it then affects everything else you build afterwards.
It's super interesting that you have this definition given your authorship of django (I mean, actually interesting, not 'interesting' :-)
In another comment I used the example of rails as a kind of canonical 'framework' that can potentially do everything for you full stack, and django is in the same category, juxtaposed against something like React that cannot.
To that, I think your last paragraph is the one I agree with most closely. It's true, but only for the view part of the app, right? I think that's where I get stuck on stretching to calling it a framework.
I guess I can see it if you're defining your view/client as a separate logical entity from the rest of the stack. Which is totally reasonable. But I guess just not how I think about it.
It is best described as a pattern implementation (observer / pub-sub).
Example Node.js EventEmitter usage:
const EventEmitter = require('events');
emitter.on('data', handler);
emitter.emit('data', value);
-----------
You explicitly instantiate it. You explicitly register listeners. You explicitly emit events. It does nothing unless you call it. There's no lifecycle, no main loop, no required structure.
EventEmitter is not a framework because it does not define application structure. It doesn't own the program's control flow. It doesn't decide when your code runs (beyond callbacks you register). It does not enforce conventions or architecture.
People sometimes call it a framework incorrectly because of two sources of confusion:
1. Callback-based APIs feel like inversion of control. But this is partial IoC, not framework-level iOc.
2. It is often embedded inside frameworks. Examples: Express routes, React synthetic events, Electron internals.
2 - without prior config only a bunch can do it, like pixels
3 - there's a difference - you can configure wg/ts on a single device(router) and it's done, or you need to do it on 5X+ devices, phones and laptops and fix the configs on all if something changes
Maybe with newer models it's easier, but older ones you needed to go in some advanced settings to allow sharing the wifi, otherwise it'll disable your wifi connection. For pixels it was enough to just enable it from quick menu
Tailscale can tunnel all your traffic through a chosen exit node so you browse the web and whatnot as if you were at home (or wherever the exit node is), so in this way it's a bit like a VPN from a VPN company, but it doesn't give you a list of countries to select from.
VPN companies aren't really in the business of selling VPNs. They sell proxies, especially proxies that let you appear to come from some country, and you typically connect to the proxy using the VPN functionality (particularly if you're using a consumer device instead of a laptop), but often you can use SOCKS5 instead.
Tailscale isn't in the business of selling proxies.
You can run it on a capable router or on a RPi, or on your NAS. It's especially useful if you want to self-host (e.g. Immich). You can use it to authenticate for ssh if you like, or simply give you an IP you can ssh to.
It's especially handy if you want a secondary way in, in case you have problems connecting using wireguard, since it supports using a relay if you're stuck in a hotel with a heavily restricted connection.
If you run DNS at home, you can even configure it to use your home DNS and route to your home subnet(s).
It makes more sense if you are used to Ubiquiti ecosystem. Basically they assume you have Ubiquiti-based home/office network (they call it site). Then this device binds to this site and VPNs to it over Teleport (kinda similar thing to Tailscale, also built on top of wireguard). I would assume you can also configure Wireguard/Open VPN/IPsec manually as this is pretty standard in their ecosystem.
I guess it's nice if you are in Ubiquiti ecosystem already and want as little friction as possible. Otherwise it's probably similar to any travel router.
Yes, they sell for the aesthetic. I have real plants but some friends have fake ones and honestly at a distance without scrutiny they look just as good. Hell, certain real plants look and feel plasticky themselves due to how they're composed, especially vinous plants.
Compression algorithms for experience are of great interest to ML practitioners and they have some practices that seem to work well. Curriculum learning, feedback from verifiable rewards. Solve problems that escalate in difficulty, are near the boundary of your capability, and ideally have a strong positive or negative feedback on actions sooner rather than later.
I don't know. I built a vector similarity system for my hobby project the "hard" way, which was mostly getting Python set up with all the dependencies (seriously, Python dependency resolution is a non-trivial problem), picking a model with the right tradeoffs, installing pgvector, picking an index that optimized my distance metric, calculating and storing vectors for all my data, and integrating routes and UI which dispatched ANN search (order by / limit) to my indexed column. I also did some clustering, and learned something of how awkward it is in practice to pick a representative vector for a cluster - and in fact you may want several.
I now know what the model does (at a black box level) and how all the parts fit together. And I have plans to build classifiers on top of the vectors I built for further processing.
The experience of fighting Python dependencies gives me more appreciation for uv over venv and will leave me less stuck whenever the LLM fails to help resolve the situation.
Right :-) That article did read as satire; but then, it is hard to tell what is satire anymore. I can hear people say the things he catalogues in the article; and I might agree with some of them, like the pollution of the window namespace, or the ancient syntax if this is indeed the case.
I could then open up the postscript, delete the commands that rendered the watermark, save it, then I converted it to PDF so it would be easy to print.
reply