I’ve spent five years writing a JavaScript framework
medium.com
medium.com
The JS ecosystem is pretty fragile because every project depends on hundreds of other projects.
The other day I had to go back to a 2 year old Vue project. I found that many of the dependencies had vulnerabilities so I started updating those. Then nothing worked because the newer versions changed its API or didn't support such and such feature. In the end I had to reconfigure all the project from scratch. And this project was just 2 years old...
I don't mean offense, but JavaScript is just so accessible and easy to get into, that's it's flooded with people who haven't had time to learn the instincts that make a senior dev a "senior". The author of seems to really "get it", as do many others.
to an extent that's why I don't understand the jQuery hate from modern JavaScript devs, jQuery had a stable API for years both externally and internally, so that not only it easy to upgrade it but it also easy to upgrade whatever plugin you were using.
sure it's not a framework so it doesn't really reduce how much code you write, but it's a solid foundation library
There is no need for the library ecosystem and dependency culture around a popular programming language to be as fragile as JavaScript's. Other popular programming languages manage not to have this problem to anything like the same degree.
If you hadn't used those libraries, you would have had to implement functionality from those libraries in your own code. Presumably, your code would be subject to the same vulnerabilities and probably require even more work to update. After all, API changes are generally easier to handle than design changes.
</advocacy>
I do agree that tiny, insignificant libraries like "left-pad" are bad. Imo, a good dependency is one that significantly reduces the design complexity of a project.
Same, but it's nice if those libraries have zero dependencies so when you include them in your project you know exactly what you're getting.
I must admit I have the same aversion as OC. I love the ubiquity, resources and portability of JavaScript, but the rats nest of dependencies feels extremely brittle.
There are universal utility libraries. jQuery for DOM, underscore for general utilities. It's OK to use them, because everyone knows them. But if you miss some tiny function, just write it yourself. DRY principle often brings more harm than good.
- have few dependencies
- favor dependencies that, themselves, have few dependencies
- favor dependencies that have a single purpose
- favor well-understood stable libraries
- favor libraries that handle breaking changes well. A good Java example is commons-lang 2 vs 3 where the package name was changed so they can coexist. A bad example is Guava.
In general; many deps is a bad idea for longevity of software. How can you know if it does not all crash and burn if the author changes jobs? Personally I want to know I have the sources and understand them enough to be able to support them with our team; in reality, for the JS ecosystem, that often means it is less work (those hours you mention) to write it yourself because it was trivial to begin with anyway and the npm solution has 1000 dependencies yours does not (for instance, they use deps like left-pad to implement something that can be implemented in 100 lines of pure JS, now needing still 100 lines but of rancid (unsupported?) one liner ‘libraries’ which is absolutely insane)
Managers usually are not programmers nor have technical background: once something works, they might not allocate time for you/your team to touch it for many years. You probably left or enjoying your pension (many anecdotes about that: a lot of companies are running on software that was done by one employee and he retired, years ago), while some poor junior who does not know what JS is, is holding your turd. At least with 20 year old PHP it still runs and without knowing PHP beforehand, it is not hard to change/fix for a capable programmer.
I have seen this with larger node.js and with Rails projects; not so much with Java and .NET projects; the latter seem to be fine many year later (maybe with some tiny migrations), even when updating libraries or runtimes.
It is a compromise ofcourse; I am not against reuse, but only to the limit we could possibly handle maintenance and updates ourselves of all imported libs. Otherwise it is a no-go.
many of these only work when installed globally and conflict each other version to the point the only way to work at project of different ages are chroots or flat out vms. whomever ever thought that -g was a good idea should never work at a tooling system again.
heck, some years ago you could push a library change to npm updating a version number that already existed there, the place is a mad house.
Sure, but a lot less time that planning, programming, debugging, maintaining your own library.
> In general; many deps is a bad idea for longevity of software. How can you know if it does not all crash and burn if the author changes jobs?
Many many times if a library doesn't work there are about 100 other ones that do the same thing.
> At least with 20 year old PHP it still runs and without knowing PHP beforehand, it is not hard to change/fix for a capable programmer.
How does it run? It's not even maintained anymore nor installable in any modern server. You can run it, and be exposed to all kinds of security holes.
> It is a compromise ofcourse; I am not against reuse, but only to the limit we could possibly handle maintenance and updates ourselves of all imported libs. Otherwise it is a no-go.
There's a much higher change of a single developer falling out of love/not having time for a project anymore than a major library becoming unmaintained or having a serious bug unfixed.
If that happens, you just swap it with another one. It takes 1/100th of the time compared to do everything yourself.
1. usually a library has a wider range of features than you need. So you don't have to rewrite the whole library, just the bits you need, and you end up writing much less code because don't have to integrate the bits you need with the bits you don't need.
2. you do have to spend a lot of time understanding the interface of the imported library and writing integration code to use it correctly.
3. (as others have said) there's a lot of time spent auditing the library (and all its dependencies) to make sure it's not doing bad things. And that auditing work needs to be repeated every time any dependency of the library updates a version. This is especially important if you're doing anything with customer information involved - you're liable if you import a library that imports a dependency that quietly sends your customer's personal information to a url in Russia.
4. you may have to make architectural compromises to fit the library in; it might not support concurrent access, or idempotent calls, and that may cause more time loss in the long run than just writing what you need yourself.
5. the library becomes a black box, and your code becomes plumbing connecting the black boxes together. Trying to debug your application, or improve performance, or make it more robust, is impossible because you can't change any of the black boxes and your plumbing code does nothing important.
that's most of why I prefer to roll my own code rather than import a dependency in most cases (crypto being the notable exception).
https://www.theregister.co.uk/2019/03/28/hcsec_huawei_oversi...
Another thing is people either:
1. Use their own boring abstraction every time, and every abstraction might be different. 2. Not using abstraction at all, this makes JavaScript more like C code base.
Of course, I'm not saying it's not fragile now. But probably the answer is not as simple as dependency free. Some dialect provides interfaces (like TypeScript), type-classes (yet to be seen in mainstream dialects but I believe this really helps when dealing with fragility because Java could also be fragile because of nominal typing without having type-class) might help in the future.
Seems like you did something similar with regards to building web applications (5 years working on this framework). Good for you! And if you can convince others to use your stuff, all the better!
I'm trying to focus on writing my own tools too for the kinds of projects I work on, including fronted development (instead of always be trying to catch-up with the latest frameworks) [1]. Can't completely ignore the trends or fashions, though, sadly. A hard balance!
One of the selling points of React/Vue was that components were portable. But I have seen so few people actually re-use anything in the real world and you didn't need a library to make re-usable components to begin with.
I believe that re-usability isn't a language or framework feature, it has to be a personal or team choice to prioritize it. It could be as simple as a utils folder or as organised as a bunch of modularized git repos but ultimately you have to choose to organise that way.
I'd love to have this sort of confidence in my ability to foresee everyone's future needs, but I don't. I mean, I agree that having to do full rewrites every time your framework's version bumps, but promising no changes ever seems more constricting than necessary.
The full quote. Note the words "completely rewritten".
You're misrepresenting what the author said (arguably the lead in was slightly exaggerated or just poorly written) when you seem to agree that full rewrites are bad.
Look at jQuery. We are now in a new paradigm and jQuery hasn't changed, and should not change.
I like template engines to some degree, and I love binding like you find in Angular. But the first few times I read Angular code, I didn't "get" it, because it seemed like there was some magic somewhere. Some automatic things that happen without being explicit. (This was particularly prevalent in AngularJS, in my opinion.) Never a fan of that. So an explicit coded view would be preferable. But I think it's probably going too far towards trying to make web views in code. Similarly, I dislike React for that reason. I'm not religiously opposed to markup... HTML and CSS from being controlled in your code. But I still struggle with it, and prefer to do what I can to keep them inside templates, as HTML and CSS, and not some abstraction.
I do love TypeScript, so for my purposes, I'll happily use Angular 8, having never lived through the original AngularJS 1.x -> Angular 2.0 transition that made so many developers unhappy. But I appreciate what you've done, as well as respect the work and time you've put into the documentation web site. Cool stuff!
> Have you ever opened a years-old project that used the favorite Web framework-du-jour, and tried to make sense of it now? Would you be able to maintain such a piece of software?
Yes? Even if the answer were no, why would this framework be any different? The author seems to suggest that his perspective on "maintainable" is universal when it's actually just his opinion. Having strong opinions is fine, we all have opinions, but trying to play up your own framework as if it's objectively more maintainable is eye-roll worthy, especially in the context of JS land.
> Who will remember the (hypothetical) peculiarities of the willUpdate method in version 14.2.132 of framework X?
This is a banal criticism that can be applied to any piece of software that introduces breaking changes. The idea that the author will never release breaking changes is absurd, especially on the web.
> Code shouldn’t have to be completely rewritten to be compatible with the next major version of Typescene
"completely" and "compatible" are pretty vague caveats. Is it a complete rewrite if I have to rewrite 25% of code? 50%? 60%? I'm sure someone can point out an example of a framework that required "a complete rewrite" in order to be "compatible" with the next major version, but this isn't typical.
> No-nonsense object-oriented (OO), event-driven approach.
OO is nonsense (someone's opinion).
> you’ll only need external modules to import complex UI components or application behaviors that aren’t included in the framework itself.
You'll only need external modules for complex UI and behaviors that aren't included in a minimalist framework... So, just like every other framework out there.
> Most importantly, Typescene hasn’t been invented overnight, it’s not a Minimum Viable Product that introduces some clever new paradigm
What is an example of a framework that was "invented overnight" that introduces some clever new paradigm?
I'm not trying to come down on the author, and I'm not making a technical criticism of the project, but the "everyone else's shit stinks but mine smells like roses" is a turn off, for me personally.
> trying to explain why their project has advantages
IMO you can do that without the tacit suggestion that the competition produces unmaintanble code, or at least, explain why this framework is objectively more maintainable and not just one's particular opinion of what is maintainable.
> if a project took an arrogant tone while also having a large amount of money backing it, I might be annoyed
I'm not really annoyed either way, but in my totally subjective opinion a lone developer should take a more humble tone, it's not as if backing by a large corporation correlates with non- maintainability.
The JSX version is more readable tho. It looks like HTML. Everybody knows HTML.
The real feat here is getting rid of the hacky way that XML and/or template strings and JS are mixed together in the compiler, without sacrificing readability per se.
Imho the proposed way sacrifices readability a lot (what does "with" do? is it like the infamous JS with? what is the first, second, third, ... argument?).
JSX is a de-facto standard, and it might be convenient in the way that PHP is convenient, but that doesn't mean it's not inherently a hack :) ...
onClick: "checkTodoItem()"
Why? What compells people to go ahead and say: yes, we’re writing everything in JS/TS which has perfectly fine support for actual functions. That’s why we’re going to arbitrarily use strings that evaluate to function calls in some parts of our framework.[1] See views https://typescene.dev/docs/introduction/overview
Anyway, yes Typescene does let you supply an actual function in place of the string here, but for me that ruins the 'neat' flow of the view and starts mixing views with logic.
onclick: () => function_to_be_defined_at_runtime()
This also lets you deal with functions that are defined at runtime. Moreover, this actually lets you provide types for the expected callbacks.> but for me that ruins the 'neat' flow of the view and starts mixing views with logic.
How is providing a reference to a function "logic"? You still provide a reference to a method, only you do it in an unspecified stringly-typed DSL.
Be sides, you're using these magical strings in so, so many places: https://news.ycombinator.com/item?id=20310666
This is anything but strongly typed. This is stringly typed.
Looking at the code doesn't seem specially appealing:
export default UICell.with(
UICenterRow.with(
UILabel.withText(bindf("${foo}, world!")),
UIPrimaryButton.with({
label: "Do something",
onClick: "doSomething()"
})
)
)Also, is it really as strongly typed as claimed, when the onclick binding in this example is stringly typed? That's a nonstarter for me.
Edit: Found it, it's called RealWorld (https://github.com/gothinkster/realworld)
Question - do you have financial goals with this project, or do you see it as a community / hobby project? If so, what are your plans to do that?
Have you made sure you got authorisation to release the code, or that you were in a case where you didn't need such authorisation? Because in a lot of cases the code is not the developer's property, it belongs to the company who paid him to develop it.
The search popup is implemented using Typescene though.
The only elegant thing I find about this framework is that it has no deps. A TypeScript-first framework would be a plus but I find the Typed API particularly inelegant with its usage of static methods to compose the UI which is particularly non idiomatic for JavaScript.
There doesn't appear to be any live examples you can experiment with which is a notable shortcoming for a modern JS framework and the only complete stand-alone example I can find is something called "first project" that was contributed 19 days ago:
https://github.com/typescene/first-project/
The name doesn't fill me with confidence and the only example I could find has a webpack dependency which kind of throws out its "no deps" USP. I don't find anything compelling in that example that would entice me to use it over React, Vue or even just Vanilla JS - which I'd prefer to implement this particular example of which I'm not clear what value it adds that would justify adding a dependency to this JS FX.
This file is the most code I could find:
https://github.com/typescene/first-project/blob/master/src/a...
It's full of "magic string" APIs where there's no way you could intuitively guess what the values would be, e.g:
- maxWidth: "100vw"
- gravity: "center"
- revealTransition: "fade"
- borderColor: "@separator"
The benefit of using TypeScript is that all these values could be typed to assist during development and type-checked to validate any runtime errors.Magic strings are even being used for callbacks that looks like it uses its own unknown DSL in different APIs of which is going to be unknown to anyone but the author:
- onEnterKeyPress: "addTask()"
- state: bind("object.complete")
- onClick: "+ToggleTask"
- tl("{@text/50%}${todo.nRemaining} task#{/s} remaining")
- hidden: bind("!todo.nCompleted")
The docs are particularly lacking in working examples of which the bulk looks like an API class dump which isn't a useful onboarding experience for anyone who wants to learn and use the framework.Honestly if headline didn't say it took "5 years" to implement I would've guessed its a few months effort max - nothing like the refined, battle-tested, real-world validated framework this article proclaims it to be.
This is where it lost me. Why is elegance a priority? What problem is elegance trying to solve here? Elegance is just a feature, that ill-suits many people who might be better served by others. Also seeing the examples in the homepage don't seem very "elegant":
// A first Activity, similar to the Controller in an MVC approach
export class MainActivity extends
PageViewActivity.with(view) {I think what's happened in these cases is, typically by luck, you've written code that matches up very simply and beautifully with an undescribed and perhaps indescribable pattern that's present in a larger problem domain. Like poetry, your code expresses the nuances of that problem in a way that prose simply can't (efficiently). You can't always tell or explain what's so great about it, but working with it is a pleasure, and it rarely lets you down. To me, that's the ideal of elegant code.
E.g. see this:
https://developer.android.com/training/basics/firstapp/start...
Compare it to OP's framework documentation.
Writing 'new Button' for every single UI component would be really cumbersome, that's why there's also a 'template' syntax which uses static methods (Button.with(...) which creates a button factory).
The introduction bashes some malpractices of other frameworks, but it doesn't tell me why I would use this framework over Vanilla.js.
I would refrain from using this term since it doesn't have a single agreed upon definition.
There is so tremendous time waste in developer world just inventing new langs or rewritting new frameworks that do the same.