Hacker News new | ask | show | jobs
by zer00eyz 743 days ago
I moved from PHP to Golang a bit over a decade ago... Because binary deploys are the bees knees.

I dont want 8 containers isolating a bunch of bad software or worse messed up dependency's (looking at you python, maybe some ruby and node code as well). Because rather than releasing installable software you packaged up "works for me" and shipped it to the world.

I might play with this out of nostalgia but not so sure that I want these sorts of things in my production environment any more.

7 comments

This is PHP running inside a Go server, btw. Compiled with CGO. One binary, one process.
Wow the PHP is going to magically get into the binary?

/s

Yes I could build this with c-go + the PHP and statically link everything (and there might need to be a bit of prayer involved...) ... or I could just write GO and accomplish all the same things with fewer steps.

That knotted up mess is why containers are popular.

There are things Go excels at and there are things PHP excels at. They don’t always overlap. For example, you can write a simple web app in 100 lines of php code, and a process manager in 100 lines of Go code. If you flip the languages around, the number of lines explodes.
I started writing PHP back in the 3 and 4 days... I have been writing go for over a decade.

If I had to add a few dynamic elements to a static HTML site, php is still king.

If we're building an API, go will win hands down. Not only in LOC but performance, and reporting.

When you get into whole pages (think blogs or shopping or... ) a lot of that is going to depend on what framework you're grabbing on the PHP side. There arent a lot of "batteries included" solutions on the golang side... You might end up writing fewer lines of code in the first week but by the end of the month were going to be at feature parity and your quickly going to fall behind.

The moment I need a SPA, on page interactive GUI you're going to end up with a JS framework and an API back end... here again, go will shine.

> If we're building an API, go will win hands down.

I'm not so sure of that. Writing business logic in PHP is usually more concise than in Go, and more flexible. Also, I can think of a number of json schemas that are impossible to replicate in Go's type system but work just fine in PHP.

>> Also, I can think of a number of json schemas that are impossible to replicate in Go's type system but work just fine in PHP.

Not at all. There are some that would be painful to write "by hand". An expansive and nested set of null fields would suck if I had to spell it all out...

https://sqlc.dev << changes everything. If you add in the YAML (and I hate yaml) you can get your JSON to DB mapping in there, as well as your validations (all output as struct tags).

Everything else that you're going to want (transforming inputs to/from json, logging, auth) is some pretty simple middleware.

You can't fully replicate JSON schemas in either language without writing logic.

In terms of just static definitions, Go has typed container types while PHP has not. That's a pretty common case for JSON. PHP is more convenient at another common case which is heterogeneous maps. Neither of those languages make me particularly happy in that regard.

It also depends on what you're doing with the JSON. If you want/need to walk through it in a single pass fashion, then Go is definitely more fitting.

If you use the std lib you can write a simple web app in Go as well in few lines of code?

The only difference would be a little bit of boilerplate for setting up application state (a few lines) and the necessary type definitions, which you can avoid with PHP if you just deal in "arrays" instead of classes. But I don't think the LOC would explode for Go.

My big counterpoint is this however:

I would wager for a beginner of either languages, it's much more straight forward to get a server running with Go than with PHP/Apache. There's no configuration, you don't need to install multiple things, you don't need to learn about a thousand footguns, error handling is more streamlined and obvious and you wouldn't have version issues and get showered in deprecation warnings down the line. And even though the PHP docs are quite extensive and useful, they are not on the same level as authoritative Go docs and guides. PHP has three advantages over Go in terms of convenience and ease of use: statelessness, little bit easier to deploy (once set up) and very cheap, easy to use hosting products available.

> If you use the std lib you can write a simple web app in Go as well in few lines of code?

PHP is made for web development though, while Go is made for different use cases. Comparing the standard libraries makes this very clear.

Things PHP comes with that Go doesn't come with (just as some examples about standard library differences): PDO (DB interface), session management, built-in mail sending, localization and internationalization. That's just from the top of my head.

Go has it's places though, and it works well for web development too. But even I who like Go in general more than PHP, can acknowledge that PHP (by default) is better setup for web development out of the box.

That said, I would much prefer Go than PHP too, but hard to argue against PHP for web development without resolving to emotions and feelings.

Apart from localization all of the things you mentioned come with Go as well. There's net/smtp, database/sql. You can implement in-memory sessions in Go with a few lines or use the well established gorilla sessions or similar.

The only thing you _need_ to install via dependencies is a DB driver.

On top of it you are getting a HTML template system with very good security defaults. You are generally better guided and guarded when dealing with HTTP user input. Routing and handling is more explicit and doesn't require you to deal with the Apache configuration syntax etc. There are no arcane configuration footguns like max_input_vars that will silently break your application and so on.

Errors handling and logging are again, more straight forward, universal and explicit.

If you need interactivity, SEE and Websockets are not only easier to use but also a more natural fit for Go.

I think for a language beginner those things are very, very beneficial. Us more experienced programmers sometimes forget how many battle scars we had to earn with languages like PHP.

You can write a simple web app in a tiny number of lines in Go as well. Try templ
> Wow the PHP is going to magically get into the binary?

It's fairly simple to pack assets into a binary. (Not sure, if they are doing that. But it wouldn't involve any magic.)

Compare also https://doc.rust-lang.org/beta/core/macro.include_bytes.html

Btw, making a tarball or a zipfile is also a way to put multiple files into one binary file. You might have even heard of self-extracting zip files? They were very popular, before Windows got a build-in unzipper, I think.

Optionally, you can embed the PHP scripts in the final binary using a Go feature similar to `include_bytes`: https://frankenphp.dev/docs/embed/

But for PHP, it's even simpler: PHP is available as a C library, and we just use it. (libphp can be embedded using static compilation, or used as a dynamic library, we support both).

> But for PHP, it's even simpler: PHP is available as a C library, and we just use it. (libphp can be embedded using static compilation, or used as a dynamic library, we support both).

Yes, but that's completely independent from whether / how you embed the scripts? (Which you do via the the procedure described in your first link?)

Btw, how does your version of PHP compare to Facebook's Hack? I had opportunity to use Hack a few years ago, and I think it's the best language they could have made (from PHP as the fixed starting point).

Well, FrankenPHP avoids that prayer, cause it's already done for you lmao

But obviously the point of using PHP is for its ecosystem, frameworks etc. Go is still missing a lot of that, the Go community has a weird aversion to frameworks.

I wrote PHP from version 3 to version 6... I remember PHP before it had "frameworks"...

PHP ate perl because the battery's were included, you didn't need CPAN, and it had an amazing manual...

Python (pip), Ruby (gems) and Node (NPM) all looked much more attractive because they had rich library's at their core and symphony wasnt really useful yet.

Yet Go eschews a lot of the same package management issues those other languages have. And a lot of go devs look at the standard library the same way early PHP devs looked at it "I dont need a lot more than this".

It's not that go devs are framework adverse we only want what we need and nothing more. In a language that favors composition frameworks quickly become "baggage".

It's kind of interesting to watch someone come from a language with a strong package ecosystem and watch them build their first go project. They tend to cruise GitHub and throw EVERYTHING in the cart. It takes them 3-4 days of working with all that before they are complaining up a storm! You hand that same dev SQLC, Validator, and point them at middle ware and the standard lib and ask them for API's and they come back 2 days later grinning ear to ear.

Go, done well, is brutalism. Blocky, functional concrete buildings have an amazing charm to them... Go is blocky, its utilitarian, its expressive... You dont write cute code in go, you dont try to make go magical, you want clear easy to read and understand code... Your more likely to come back and add features long before you have to address the performance of go.

> version 6

PHP6? That was long ago because I don't even remember it!

I don't have much to say otherwise, except you could very well say the same "Go is great" without having to shit on PHP and the project that is being discussed here.

Especially since you don't really come off as very knowledgeable on what you're talking about.

It might shock you that I wrote perl before PHP... I have been in the game a LONG time. I was around for php being the language that EVERYONE shit on. I was around when PHP got somewhat huge and respectable. PHP buttered my bread for a number of years and I was one of its bigger advocates.

My lament here isnt about PHP at all.. It's about the instructions here: https://frankenphp.dev/docs/embed/ to generate a monolithic binary... It's about the execution of this script https://github.com/dunglas/frankenphp/blob/main/build-static...

None of that is very go like at all. Having spent a lot of time with PHP I recognize a good portion of what turns up in that shell script. That having been said there are parts of it (the curl of the diff) with NO context that I would pause and go figure out what the hell that was. It really needs a note as to WHY it's there so I dont have to go hunting. It looks like the shell script is meant to work in the docker container... A real build system might be in order here (make, basel, mage).

> I wrote PHP from version 3 to version 6.

There was never a released version 6.

https://wiki.php.net/rfc/php6

for the same reason as Windows 9 - it would have clashed with PHP '68

/j

PHP 6 was never released.
This is the one good thing non-Go web developers are jealous about Go.
.Net and rust and many other languages also support binary deploy. You raise the impression this is a unique property of 'go', how come? You guys have go tunnel vision because you work with it?
Go started it. Or at least, popularized the static build, single binary docker files.

Naturally, you’ve been able to do this since forever with many languages like C/C++ etc.

Among people that have used nothing besides scripting languages.
To be honest, it was and is pretty rare to see C/C++/etc. self-contained static binaries on Linux. Glibc doesn't even properly support static linking, and musl does not have 1:1 parity with glibc, so it's challenging to even give a "single binary" for C/C++ programs on Linux, less in the past before musl and during musl's infancy. The more popular thing to do on Linux is something more like AppImages where you bundle a bunch of stuff into a single self-executing file, and that ultimately still is a dynamic binary depending on system libraries (and therefore can't be e.g. plopped in an empty Docker container.)

(And yes, Windows and macOS, and probably most of the BSDs, don't really suffer too badly from this issue, since dynamically linking to system libraries/libc is not a problem. Still, not needing libc remains a big advantage for Go since it is possible to cross-compile self-contained Go binaries to any platform Go can target from any other platform; this is challenging to do with C/C++, especially for targets like macOS.)

Even if you do get past the code though, there's the assets. For C/C++, bundling static assets into an executable is oddly challenging. There's been a bunch of different bin2c-type programs over the years, yet somehow not really many "standard" approaches to doing it, and many of them suffer from bad performance (especially approaches that go to a source file instead of an object file). Just now, finally, in the past couple years, there is #embed in C... for embedding a resource file. Meanwhile, Go has had several good options in the ecosystem for embedding entire directory trees and using them inside of VFS layers for ages, and since Go 1.16 (2021) it has support for that as part of the toolchain.

Go didn't invent the concept of making a single self-contained executable, it did however push the status quo quite far. I contend that I have the experience to make this claim.

P.S.: To be clear, I'm definitely not saying you literally can't, and I am aware of Cosmpolitan libc and APE, which are pretty cool. Of course, with effort, you absolutely can make static binaries using C/C++. I just recently did this with a C++ and Rust program using musl; it's tricky, especially since many build setups don't really support these kinds of deviations, but it's possible. One major problem I have with this is that I don't really feel very confident in the stability of the resulting binaries; if something calls dlopen with static musl, it will crash, and behavior of certain things like DNS resolution also changes in ways that may break some programs. The reason why Go's contributions matter is that for basically as long as I can remember, the way to do this in Go is to install the toolchain and run `go build`, any host platform, any target platform, and it's well-supported by virtually everything because it is the default behavior. (And if anything, Zig is closer to bringing this to C than any of the C/C++ toolchains...)

All self inflected problems in the GNU/Linux ecosystem, that don't change the fact the computing world since FORTRAN was introduced as first compiled language in 1957, static compilation was a thing.

Also embedded resources has been a solved problem in most non UNIX platforms for decades.

Rust and Zig also do compile to binary. They just weren't part of the stack in thee article...

I dont think I would use either of them in the same places I want to use GO... Things that were java, or python are candidates to be written in go. Things that were C or C++ are good candidates for Rust or Zig.

Look at cloudflares nginx replacement, it's written in rust for a GOOD reason. Thats not a place where go will shine being a GC language.

.Net only recently added support for it and it's very brittle.

Rust is indeed in the same league as Go, I guess the difference is that Go is batteries included when it comes to web development. You don't have to pull many dependencies, unlike rust.

Rust isn't so bad... A lot of devs have started to normalize around Axum/Tokio, which is pretty well supported. A lot of boilerplate API stuff is about as easy as it is with Go after a project is up and running.
No it’s not. Self-contained deploy has been a thing since .NET Core 2.1 (or earlier?), released 6 years ago. Either way, deploying .NET applications has been very easy regardless of deployment mode for as long as they were using Kestrel.

Today, it reached the point where .NET outperforms Go in publishing/packaging of AOT binaries as it can be statically linked together with native dependencies, metadata tables and pointer-heavy structures in the binary are dehydrated and compressed and the resulting binary uses standardized object and symbol format understood by all existing tooling that otherwise works with C, C++ and Rust.

That was not my experience when I worked with .Net, IIS and the whole thing but it was a long time ago so good to know :)
How long ago? Nowdays most people dont use ISS for docker builds, it usually runs on Linux containers.
.Net is a lot more sane now... since Core releases (mostly). Linux deploys with docker are pretty typical these days.
Go popularize it, and still leads in the easiness of cross compilation.
Many non Go Web developers know about static compilation, before shared objects were even a feature in mainstream OSes.
And charm.sh
Right, so CGI is quite old by now, which is basically this: a binary in a directory and a proxy that throws packets at it.

It has its pros and cons. In some cases it's actually quite nice to have a platform like Wildfly or the BEAM, or you knew so little about the future load peaks that you started with cheap shared hosting and hence PHP so when you outgrow it FastCGI and nginx is a much better option than a great rewrite to Golang.

Building in the languages and on the platforms you already know will always be much faster and more convenient than betting on someone else's 'bees knees'.

PHP had the unique fun of when an attacker was able to write files to the server they could literally write new PHP files and get them to execute. I miss that worry somehow
You can do that or something similarly bad in pretty much any language if you have write access to where the files are. That's really a server misconfiguration problem, not php problem.

Huge misfortune of php is that because of the timing and huge popularity in relatively early days of web it got to be permanently associated with amateurs doing stupid shit. I was part of that early era, so trust me that php4 as bad as it was, was still fantastic compared to writing CGI scripts in C, or running mod_perl that you had to restart every time you edit some file.

f-that... read-only containers for the win.
why would it be 8 (or any N>1) containers for a php/node/python/ruby app?
Python is pretty messy to deploy without a container. Sure you can (and should!) use a virtual env, but by that point using docker isn't much more efforts.

Rails and Node and PHP have at least the decency to have the project's dependencies contained inside the project's own directory by default.

That’s the default of the package managers (i.e. composer, npm, etc) not the language. Setting up a virtual env for Python is a one liner. Hardly messy.

I’d argue that deploying a Python web app with mod_wsgi or gunicorn isn’t any more difficult than deploying a PHP web app with PHP-FPM. If anything I’ve seen far more misconfigured PHP-FPM setups than gunicorn setups.

nah not questioning containers. questioning why he wrote 8 (obviously exaggerated) and why it’s not 1.
Nginx/Apache/Caddy + php-fpm + something like MySQL if your database lives on the same server as your application. PHP in its default form isn’t a persistent process[0], so you will typically have a web server accepting the requests and forwarding them onto PHP to generate a response.

0: it actually usually is these days for performance reasons, with a pool of processes that are reused. But the mental model is one short-lived process per request

and if the Go dev needs a database, or a cache, or an actual fully-featured HTTP daemon processing the requests?

I’m just confused about their focus on “single binary” when applications in those languages tend to be a single-container plus the same supporting services their single binary is going to need too.

did you miss this part in Frankenphp website ? :)

  Build standalone, self-executable and dependencies-free binaries for your PHP web applications, and command-line tools.
I’m a total noob in Go but is there something that approaches completeness of the good old monolithic frameworks ?

I’m in a pause in my career and I’m having a great time working on a "side" project in Django currently. Everything just works and makes sense, I just have to write business code and it gives me a nice app.

I’d be happy to try Go for fun and because deploying Python/Diango is a mess (at least without docker) but my project isn’t API first, it’s deliberately a traditional server side rendered app.

Every Go frameworks I stumbled upon looked like they were made to serve APIs and In the case of a 1 man team, it defeats the purpose of easy deployment since you will anyway need to code and deploy a full blown frontend (and front end fatigue is a big reason why my career is currently in pause).

GO + HTMX + standard templates... or some folks swear by templ

On the API side (not what you wanted, but good looking) is sqlc + validator

Between all of those you have everything you want for a website.

Week 1 go is going to be a lot slower than Django or Rails. You're going to have a bit of bootstrapping to do for your project. By the end of the first month you will be a week or two behind but performance parity will be the same (you should have roughly the same feature cadence).

A good framework brings a lot to the table. With go you have to "solve" for that yourself. "How do I log a request" would be one of those first questions... the answer is "the standard logger is pretty good" and "Middleware is awesome". It's a question that has a simple, short, very common answer. One that works for web apps or CLI apps or ... It takes a bit but you end up embracing the idea that less is better

Was going to say the same... Similarly .Net (razor views) + HTMX is pretty nice as well. There's YouTube content for both, on the .Net side, there's some JetBrains extensions to make the HTMX experience relatively seamless.
Mind sharing bit more on this?

> because deploying Python/Diango is a mess (at least without docker)

I'm playing around with FastUI (disclosure - I'm not a programmer at all), so I'm in "day 0" position and would like to hear more on "day 1" side of things. Coming from Ansible side - it's easy peasy, just install things with pipenv and later `pipenv run ...`

> I’m a total noob in Go but is there something that approaches completeness of the good old monolithic frameworks ?

Some of them try, but it's really hard to beat 20 years of development, real-life use cases, third party tools and docs, etc.

> deploying Python/Diango is a mess (at least without docker)

Install your django app in a venv, serve it with gunicorn behind apache/nginx... done. No docker required.

> Every Go frameworks I stumbled upon looked like they were made to serve APIs

Indeed, that's pretty much what Go was made for. And it's also the current trend.

> my project isn’t API first, it’s deliberately a traditional server side rendered app

Stick with Django then, because it remains an excellent choice for such apps. And these apps remain an excellent solution to a vast array of problems.

> front end fatigue is a big reason why my career is currently in pause

I'd argue you don't have to follow this trend to be useful. Actually, something like Django+Unpoly or HTMX probably covers 99% of the use cases with very low frontend development.