Pushup creator here. We're interested in feedback on the design. Pushup introduces a new lightweight template syntax that mixes Go code and HTML in the same page; pages are then compiled down to pure Go, relying on the net/http package. Pushup uses file-based routing, so adding a page adds a route to your app. The template language has an "inline partials" feature to make it easier support for modern hypermedia libraries like htmx in mind. Would love to know what the Go web development community thinks.
"Pushup uses file-based routing, so adding a page adds a route to your app."
Make sure to have a clean way of escaping that and having some richer concept of how to route. This was the original way routing worked, back in the ASP (not ASP.net, the predecessor), CGI, original PHP days. We moved away from it for good reasons. It works well early but has scaling problems over time.
I'm not saying don't have it as a default per se... just make sure you have a "if you need to do something else here's how to override it".
I also strongly advise against trying to guess what the overrides might be and building special cases for "the three things I think people may need to do beyond thsi". I've got a router in my system that looks at client-side TLS certificates if it comes from certain IPs and uses LDAP authentication if it comes from certain other IPs, and does different things if the auth fails for each case. You'll never be able to guess all the possible ways people want to route. You need to make sure you don't lock the users into this so hard they can't escape except by leaving your framework entirely.
Edit: Also, I didn't look into this deeply, but if you throw away the ability to have middleware on specific routes you've really caused a big problem. The Go ecosystem is fairly dependent on that.
> If you throw away the ability to have middleware on specific routes you've really caused a big problem. The Go ecosystem is fairly dependent on that.
Agreed! Pushup pages compile down to structs that implement http.ServeHTTP. And an entire Pushup app is just a http.ServeMux too. So it should be trivial to insert middleware (although there isn't Pushup syntax for that yet), and/or embed a Pushup app in a larger Go app (this is quite possible today).
I have the same instinct (JSP / JSF had the same concept) but I must say, making pages as files in next.js is really nice. You can have all your dynamism within them, but having a simple way to do overall routing is lovely.
>It works well early but has scaling problems over time.
How?
Otherwise calling this out as cargo cult. As I remember the main reason we moved to app routing is because of the single process nature of non-blocking async code which was popularized with node / express.
I’m Finnish. The caret is not always easy to type in my experience on international keyboards. Not sure what the added value is of creating yet another convention here, as there are so many already with each new templating language.
I spent a lot of time in C# and ASP.Net, and I was struck by how closely this resembled Razor syntax, but with ^ instead of @. Switching to @ would make it slightly more familiar to that cohort, and it might even be possible to build on some Razor-parsing code for editor plugins or whatever.
This is very neat, but you are delving into a very complex world, as you are well aware. In your video, you have generated static server side pages, without any JS, where your annotated HTML uses the embedded go to generate static HTML.
This is much nicer syntactically than using the Go html/template engine, but it seems roughly equivalent in expressive power. Are you converting your "up" syntax into go templates with the Go expressions extracted into compiled Go code, referenced be the templates, out of curiosity? If so, the way you've transparently handled interleaving (such as html elements in the for loop) is really cool.
How would your go scripting interact with JS? For example, say that I have a backend API that the fronted calls into. In your design, would I call out into Go to perform the http request, or would I do this in JS? I'm sure both would work - since the Go request would be handled server side and simply slow down static page generation, but it seems like calling into Go might not be the right thing to do for a more responsive AJAX app. Do you envision mixing Up/JS in the same pages? Can I do crazy stuff like use JS to insert a variable (by value probably) into your Go code, or vice versa?
Over the years, I've learned that web front ends are positively bonkers in the kinds of things they want to do, and when you are the developer of any kind of frameworks, you end up suffering greatly if you insert yourself into middle of that ecosystem, since you will be asked to support features that you never dreamed of. If you support them, the framework gets more use, if you don't, the cool kids move onto other things.
I've tried to tackle a much simpler problem with a project of my own [1], which is a backend server code generator to make implementing JSON models more easily from an OpenAPI spec, and I've found that even in this relatively simple concept, the limitations of a strictly, statically typed language like Go end up running into incredible complexity due to the dynamic nature of web frontends and JSON. Outside of trivial strings and numbers, you start running into the limits of the Go typing system.
Anyhow, good luck, this is very cool and it seems like a fun thing to play with.
This looks really good. I like the compilation to a single binary. I'm already using Htmx with Flask for routing/templating. File based routing with a page as the unit of functionality sit well with me.
The use of caret to denote Go code is quite clean, but given well formed HTML, could the parser detect anything not inside HTML tags as Go code and thus remove the need for a caret outside of HTML.
I will be trying this out. Thanks for this project.
This looks really promising — at first blush, I like this a lot more than normal Go templating! That said: maybe it's just my bias as someone who primarily works with React, but I'm wondering why not just mimic JSX, which has been incredibly successful and has millions of people who already understand it.
The big missing piece to me here is composability. How can I encapsulate reusable bits of markup, styles and presentational logic? In most JS frameworks, I'd extract them into a "component" (in React, that just means a function that returns JSX). Pushup has "partials," but it's not clear to me whether they're reusable or if that word means something different in Pushup than it does in other templating systems. I see that layouts can denote sections to be filled in by pages, but IMO that's backwards — a page now needs to know details about its "call site".
Using ^ as a delimiter seems odd, but I suppose that's just a familiarity thing. I do wonder though how you handle your example of
<p>The time is now ^time.Now().String().</p>
without having to backtrack, since that last period is ambiguous in this grammar.
Definitely considered that. Might be worth revisiting.
> The big missing piece to me here is composability
You zeroed in on the right things ;^) I have a draft note to myself about pages-as-components. I have some ideas (basically, mimic ASP.net Razor components at the moment). Having a compiler with full control of the page makes this easier to figure out.
> without having to backtrack, since that last period is ambiguous in this grammar.
I actually hacked together "JSX in go" over a few weeks a couple years ago, targeting wasm/GopherJS so the code could actually execute in the frontend. It was a really fun exercise in reading/understanding the internals of the go compiler.
When it comes to traditional web frameworks, the most important part is handling regular HTML forms. Reason being, a form is the only native mechanism your customers have to use to interact with your service. Forms are important. Specifically, validation and error handling. I think Laravel does it really well. It's been a while, but Laravel has a wrapper around HTML forms. This wrapper does everything you need: server-side form validation, form generation, flexible error handling that takes care of client error messages, placeholders, input values, css classes for invalid states, you name it.
Not yet, but that is the intent. As a long-time Django user I have a high bar for the kind of forms support a framework should provide. One of my thoughts/beliefs is that, as a compiler with complete control over the page, there is a lot Pushup can do on both the markup generation side as well as checks and validations. eg., automatic CSRF token insertion, compiler "warnings" for accessibility, etc.
I haven't used Laravel for many years. I couldn't find the equivalent functionality in the latest version of the framework, which is 9.x, but pretty sure they should have something similar.
So basically PHP, but in Go?
I love Go, but I honestly hope this doesn't become as widespread as PHP. There is a reason why we switched away from template-based frontends (à la PHP).
On the other side, it's also true that some frontend frameworks are partially moving back to SSR...
> There is a reason why we switched away from template-based frontends (à la PHP).
Can you elaborate on that reason? Genuine question. As you mentioned in your post, a lot of what newer web frameworks are doing (SvelteKit, Astro, Enhance.dev, etc.) are reminding me a lot of my early days with PHP. The benefits of SSR are widely acknowledged amongst the proper JS frameworks.
Frontends became more interactive, and as such, you can't really template that interactivity / reactivity away. Hardly anyone uses Vanilla JS nowadays, and thus, hardly anyone can create a template-based website for today's standards.
This is just my opinion though, happy to be proven wrong :)
One counterpoint is React is doing gangbusters right now and uses the JSX syntax extension to embed HTML directly into JavaScript or TypeScript.
To my money, the main issues with PHP had little to do with template embedding and everything to do with Perl being a messy language for writing correct code (coupled with too many people in the era underestimating the importance of "correctness" for HTML rendering when the ability for a malicious operator to mutate HTML on a page can result in all manner of security exploits). Perl just has too many ways to write accepted almost-correct code that breaks abstraction / fails to string-escape in subtle and whole-farm-losing ways.
There's a difference though. JSX embeds html into strings while the way Php intermingles html and code uses global buffers. That makes composition more complicated since you have to deal with nested buffers, flushing, cleaning, etc. I really prefer the way JSX does it over Php's.
Are you equating PHP and Perl? If so, that's incorrect - the relationship between the two is just one of influence, PHP has some influences from Perl (but also tons from C, C++, and Java).
It's a nice idea. I'm going to dig a little deeper in the design and developer experience choices.
That being said, I am a little curious -- what is the intended use case here? The reason I am asking is because I've been doing lots of web-development, with Go as a primary backend lang, for the last 9 years but never have I seriously felt the need to do serve the UI from Go. I guess there is some advantage reducing n (number of tools/languages in a stack) - but is there any other motivation behind it?
The imagined use-case for Pushup is the same niche that Rails, Django, and Flask apps occupy, which is a wide range but broadly I would describe as page-oriented, HTML-first, server-side frontends.
I started Pushup because I asked myself, I like programming in Go and think (like you) it's great for web backends - but what is holding it back from being a great full-stack web dev language or environment? net/http is a great building block, and html/template is solid, but I felt more was needed. Like opinionated project directory structure, for one. But also the world has changed since 2006 when Rails and Django debuted.
Obviously, we live in a world in which React and the SPA is seemingly dominant, but projects like htmx have recently shown that you can achieve modern UI/UX in the browser on a server-side app, which less JavaScript. BTW, more and more JavaScript frameworks, like Remix, are (re-)discovering the value of SSR.
I was also motivated to give an old-school mod_php feel of, just add a file and you automatically get a URL route. That's a nice developer experience but also good for long-term project maintenance.
Finally I was struck by this essay[1] about the demise of the "mildly dynamic" site. There is a wide diversity of types of sites and apps, from quick experiments and prototypes, small-to-medium sites of many kinds. But also since it's Go, I think it could scale up on performance to large-scale sites, and as a static binary, be nicely suited to edge apps like fly.io.
First off @paulsmith, thanks for creating Pushup! I've been meaning to learn Go, and in particular in web context...and I've always been a fan of the "mildly dynamic" approach to web sites, web apps. So, I will certainly dive in. Secondly, thank you for sharing that article that inspired you. I kept nodding along so much while reading that article, it was like i was a bobblehead! I don't code much over the last decade or more, but when i do (for web anyway), i almost always employ php in the "mildly dynamic" method...Because its easy and solves needs that come up. Thanks again!!
I have to be honest, I don't like the aesthetics. I know that's a trivial point, but if you're working in something for a living it matters. The suggestions elsewhere of using "<?go ?>" or similar look better to my eyes. I'd also be concerned that by the time there are enough features in it to support more complex sites, the learning curve will be enough that you may as well use Go directly. I could easily be wrong about that, and I know others will see value, so YMMV as we say.
That isn't to say I have any negative comment about your project on its own terms and merits, just that it isn't for me.
However I do thank you for the link to the "mildly dynamic" page. In addition to Go I also do C#, Node, Python, and others. Those others include PHP (on and off since PHP 3). For serious stuff I'm usually managed (or persuaded) into using anything other than PHP, but on the odd times I do use it the feeling is quite different from the rest and hard to explain. It feels like that linked page comes closest to explaining it for me, so cheers!
As the author of the above article I saw Pushup the other day and thought it looked interesting. If people are being inspired to bring back this sort of approach to webdev that makes me happy. Good stuff!
It's still a hard rule of mine that a website must work without JS and use progressive enhancement - I won't accept less in my own work. A lot of "modern" web frameworks get automatically disqualified when I evaluate them for this reason.
This looks great for simple projects, but I'm curious who is working on projects small enough to not be worth a separate dedicated and feature-filled frontend (ala JS/Flutter), but large enough to warrant server-side logic.
That's certainly not to say they don't exist, and Pushup is certainly filling a niche, but my mind immediately goes to those "forgotten" services like email unsubscription and confirmation links.
Here is a short demo of Pushup in action: https://www.youtube.com/watch?v=nkyiATkZ4Js
Make sure to have a clean way of escaping that and having some richer concept of how to route. This was the original way routing worked, back in the ASP (not ASP.net, the predecessor), CGI, original PHP days. We moved away from it for good reasons. It works well early but has scaling problems over time.
I'm not saying don't have it as a default per se... just make sure you have a "if you need to do something else here's how to override it".
I also strongly advise against trying to guess what the overrides might be and building special cases for "the three things I think people may need to do beyond thsi". I've got a router in my system that looks at client-side TLS certificates if it comes from certain IPs and uses LDAP authentication if it comes from certain other IPs, and does different things if the auth fails for each case. You'll never be able to guess all the possible ways people want to route. You need to make sure you don't lock the users into this so hard they can't escape except by leaving your framework entirely.
Edit: Also, I didn't look into this deeply, but if you throw away the ability to have middleware on specific routes you've really caused a big problem. The Go ecosystem is fairly dependent on that.
Agreed! Pushup pages compile down to structs that implement http.ServeHTTP. And an entire Pushup app is just a http.ServeMux too. So it should be trivial to insert middleware (although there isn't Pushup syntax for that yet), and/or embed a Pushup app in a larger Go app (this is quite possible today).
How?
Otherwise calling this out as cargo cult. As I remember the main reason we moved to app routing is because of the single process nature of non-blocking async code which was popularized with node / express.
Works great. The few times we need to get fancy mod_rewrite is pretty easy.
The lack of ceremony is refreshing.
to be fair most special characters are behind AltGr on azerty, soooo...
This is much nicer syntactically than using the Go html/template engine, but it seems roughly equivalent in expressive power. Are you converting your "up" syntax into go templates with the Go expressions extracted into compiled Go code, referenced be the templates, out of curiosity? If so, the way you've transparently handled interleaving (such as html elements in the for loop) is really cool.
How would your go scripting interact with JS? For example, say that I have a backend API that the fronted calls into. In your design, would I call out into Go to perform the http request, or would I do this in JS? I'm sure both would work - since the Go request would be handled server side and simply slow down static page generation, but it seems like calling into Go might not be the right thing to do for a more responsive AJAX app. Do you envision mixing Up/JS in the same pages? Can I do crazy stuff like use JS to insert a variable (by value probably) into your Go code, or vice versa?
Over the years, I've learned that web front ends are positively bonkers in the kinds of things they want to do, and when you are the developer of any kind of frameworks, you end up suffering greatly if you insert yourself into middle of that ecosystem, since you will be asked to support features that you never dreamed of. If you support them, the framework gets more use, if you don't, the cool kids move onto other things.
I've tried to tackle a much simpler problem with a project of my own [1], which is a backend server code generator to make implementing JSON models more easily from an OpenAPI spec, and I've found that even in this relatively simple concept, the limitations of a strictly, statically typed language like Go end up running into incredible complexity due to the dynamic nature of web frontends and JSON. Outside of trivial strings and numbers, you start running into the limits of the Go typing system.
Anyhow, good luck, this is very cool and it seems like a fun thing to play with.
1: https://github.com/deepmap/oapi-codegen
The use of caret to denote Go code is quite clean, but given well formed HTML, could the parser detect anything not inside HTML tags as Go code and thus remove the need for a caret outside of HTML.
I will be trying this out. Thanks for this project.
Caddy plugin? :D I might be quite interested in this.
There is a small demo of the inline partials feature here (click on the "view the Pushup source for this page" link at the bottom): https://pushup-htmx-template-fragments.fly.dev/demo
The big missing piece to me here is composability. How can I encapsulate reusable bits of markup, styles and presentational logic? In most JS frameworks, I'd extract them into a "component" (in React, that just means a function that returns JSX). Pushup has "partials," but it's not clear to me whether they're reusable or if that word means something different in Pushup than it does in other templating systems. I see that layouts can denote sections to be filled in by pages, but IMO that's backwards — a page now needs to know details about its "call site".
Using ^ as a delimiter seems odd, but I suppose that's just a familiarity thing. I do wonder though how you handle your example of
without having to backtrack, since that last period is ambiguous in this grammar.Definitely considered that. Might be worth revisiting.
> The big missing piece to me here is composability
You zeroed in on the right things ;^) I have a draft note to myself about pages-as-components. I have some ideas (basically, mimic ASP.net Razor components at the moment). Having a compiler with full control of the page makes this easier to figure out.
> without having to backtrack, since that last period is ambiguous in this grammar.
It doesn't backtrack, it just looksahead at the next token: https://github.com/adhocteam/pushup/blob/main/main.go#L3692
https://github.com/8byt/gox
Does Pushup help engineers with HTML forms?
I haven't used Laravel for many years. I couldn't find the equivalent functionality in the latest version of the framework, which is 9.x, but pretty sure they should have something similar.
On the other side, it's also true that some frontend frameworks are partially moving back to SSR...
Can you elaborate on that reason? Genuine question. As you mentioned in your post, a lot of what newer web frameworks are doing (SvelteKit, Astro, Enhance.dev, etc.) are reminding me a lot of my early days with PHP. The benefits of SSR are widely acknowledged amongst the proper JS frameworks.
Who is "we"? There are more websites out there written using template-based frontends (mostly PHP) than the other way around.
You say that like it's a bad thing ;^)
To my money, the main issues with PHP had little to do with template embedding and everything to do with Perl being a messy language for writing correct code (coupled with too many people in the era underestimating the importance of "correctness" for HTML rendering when the ability for a malicious operator to mutate HTML on a page can result in all manner of security exploits). Perl just has too many ways to write accepted almost-correct code that breaks abstraction / fails to string-escape in subtle and whole-farm-losing ways.
I don't like the "^"
That being said, I am a little curious -- what is the intended use case here? The reason I am asking is because I've been doing lots of web-development, with Go as a primary backend lang, for the last 9 years but never have I seriously felt the need to do serve the UI from Go. I guess there is some advantage reducing n (number of tools/languages in a stack) - but is there any other motivation behind it?
I started Pushup because I asked myself, I like programming in Go and think (like you) it's great for web backends - but what is holding it back from being a great full-stack web dev language or environment? net/http is a great building block, and html/template is solid, but I felt more was needed. Like opinionated project directory structure, for one. But also the world has changed since 2006 when Rails and Django debuted.
Obviously, we live in a world in which React and the SPA is seemingly dominant, but projects like htmx have recently shown that you can achieve modern UI/UX in the browser on a server-side app, which less JavaScript. BTW, more and more JavaScript frameworks, like Remix, are (re-)discovering the value of SSR.
I was also motivated to give an old-school mod_php feel of, just add a file and you automatically get a URL route. That's a nice developer experience but also good for long-term project maintenance.
Finally I was struck by this essay[1] about the demise of the "mildly dynamic" site. There is a wide diversity of types of sites and apps, from quick experiments and prototypes, small-to-medium sites of many kinds. But also since it's Go, I think it could scale up on performance to large-scale sites, and as a static binary, be nicely suited to edge apps like fly.io.
[1] https://www.devever.net/~hl/mildlydynamic
That isn't to say I have any negative comment about your project on its own terms and merits, just that it isn't for me.
However I do thank you for the link to the "mildly dynamic" page. In addition to Go I also do C#, Node, Python, and others. Those others include PHP (on and off since PHP 3). For serious stuff I'm usually managed (or persuaded) into using anything other than PHP, but on the odd times I do use it the feeling is quite different from the rest and hard to explain. It feels like that linked page comes closest to explaining it for me, so cheers!
It's still a hard rule of mine that a website must work without JS and use progressive enhancement - I won't accept less in my own work. A lot of "modern" web frameworks get automatically disqualified when I evaluate them for this reason.
That's certainly not to say they don't exist, and Pushup is certainly filling a niche, but my mind immediately goes to those "forgotten" services like email unsubscription and confirmation links.