35 comments

[ 3.1 ms ] story [ 74.8 ms ] thread
> all in a single file

It's not really an advantage, is it? Having everything mixed up into one giant file just makes the code much harder to make sense of.

As feedback to the author, since the author seems to also be the HN poster, I would agree with this. If it were the only meaningful file in the project, maybe it'd be worthwhile, but at 885 lines, this could stand to be broken into segments.

What I'd suggest then is adding a section to the README.md that functions as a reading guide for the code; start here, read this, here's what each file is doing, here's where this sample app is, here's that one, etc.

I agree. “Single file” projects are good for education if they’re short, but that many lines is just counter-productive.
Thank you for the feedback!! The main.go file has comments within it that try to explain what each component does, and I tried to structure it in a way that makes it very easy to conceptualize and understand. Whenever I write code, I do my best to make it self-documenting, although doing this isn't necessarily easy and I may have done an inadequate job in this instance. I'll see if I can find some time and include more info in the README.md - once again, if you read my previous response to the parent comment: the reason I included everything in one file was because of my preference, as I prefer to have everything in one place when learning something. The focus of this was teaching and demoing Go functionality, not shipping production ready code!
I recently started learning go and I've been struggling with the lack of modularity across smaller codebases like this. For all of the great tooling around formatting go, I'm kinda surprised by some of the code organization decisions I see.
There's some differences in style. I take a very aggressive stance on separating my modules; my thought is that it's relatively easy to understand what any given module is doing if it is small and focused and clearly pulls in its dependencies. But it's a non-trivial challenge to keep it all so nicely isolated and avoid loops. Worth it, I think, or I wouldn't do it, but non-trivial. There are also a lot of people who pretty much just punt and put everything in one module, so they don't have to deal with circular issues. I'm not sure I see a clear community consensus.
(comment deleted)
I completely agree, although that also depends on what you're going for. My personal preference when learning something is to have everything in one place. That way, I can easily scan through the components and see the whole piece rather than having to switch windows. I agree though that it's definitely not a best practice if you're focusing on writing and delivering production code. The focus of this app was actually showing some neat Go features and delivering a fully functional web server in a singe file. If you look through the code, you'll see that I added a lot of comments and even made notes stating that even though I included the raw HTML/CSS/JS components within main.go, this definitely was not a best practice and I referenced files within the repository which contain this code in case anyone decided that they wanted to refactor and clean it up. Anyways, hopefully this helps and thanks for the feedback!
We use a Go app called MailHog, which is a SMTP server for development use. It also has a web interface for management. All in a single go file (SMTP server, HTTP server, plus all web artifacts). That is a pretty convenient packaging for a dev/staging tool.
Do you mean a single binary file? MailHog's mail.go file pulls in a number of dependencies that round out its HTTP and SMTP servers. In OP's case, the entire project's source code is in the main.go file.
Yes, a single binary file is what I meant. I realize in some ways it is orthogonal here, but I guess it reminded me that sometimes a single file can be useful in surprising ways...
There is a widely popular web server in python that is a single file. I find it quite readable and it's well documented.

It helped me immensely on a few projects where there was no dependency management to speak of, or wasn't allowed to use external dependency. Copy a single file and done.

https://github.com/bottlepy/bottle

No reason to use a global int32 as a healthiness bit. Treat it as a dependency to the health middleware and the signal handler.
If "in a single file" means having your html and your javascript in your go file, that's not a feature...
I prefer treating a template as a constant for small projects. Trade off is it doesn't scale but vastly reduces configuration overhead.
I agree! The purpose of this was teaching, and not necessarily following all of the best practices! I made raw files which contain the go HTML templates, as well as the JS and CSS files available within the repository in case anyone wanted to reference these instead of including the raw code within main.go!!
Yeah..the Haskell one recently at least had the HTML in a Haskell EDSL instead of raw strings.
Might be worth structuring this like a normal Go project (eg. right now this is in a package called "src", which is not great) if it's meant for education. Also the listen address is actually a port, not an address. Might be worth making that a generic address so the user can specify an interface to listen on.
Having looked at writing a simple Go web service recently. I spent far more time than I'd like looking for good examples of how to structure it. Especially when many examples don't use Go Modules.
This is really cool! One suggestion: logging to a file makes it harder for things like Docker to exfiltrate logs. Log to stderr by default, and the caller can redirect to a file on disk if they want to.
Why log to stderr instead of just stdout?
Maybe just convention? It's standard practice to use stdout for directly interacting with a user and stderr for log/error type messages. You can redirect stderr to a file to get all of your log messages, but keep stdout available for interacting with the user. Super useful to separate meta-messages out in this manner.

Here's a good blog post with more info on stderr for those interested: https://www.jstorimer.com/blogs/workingwithcode/7766119-when...

Might be because stdout is buffered, so you don't always get output right away, while stderr is unbuffered.
You split the user interaction from the logging. In the case of the webserver maybe this a less of an issue, but if I'm processing text output from an application I don't want a log message inserting itself in the middle of the output.
Cool and thanks for the feedback - yup, I actually played around with deploying it using GCP and had issues due to the logging, so I definitely regret not using stderr/stdout!!
Not be to dogmatic but thus is really good:

> A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout.

Then, switching log provider is done globally, on the environment level (eg. installed on k8s)

https://12factor.net/logs

Cool - thanks a bunch for the info and the link! It looks like a great read with terrific practices :)
ok but its over 800 lines. I guess that's small for everything it does? Yet it's certainly more than the size I think when i hear "single file"
I'm enjoying these "in a single file" projects. They aren't often the paragon of best-practices but they make it easy for newbies to peep in on the syntax and see how complex problems can be solved. They are also usually a lot easier to try out, compile and deploy.

Here are a few others:

- Twitter Clone in Haskell in a single file - https://news.ycombinator.com/item?id=21616661

- MPEG1 in a single C file - https://news.ycombinator.com/item?id=20643336

- SingleFile CMS - https://news.ycombinator.com/item?id=18336986

It is an interesting idea, but it can be overkill. Just because it can be done doesn't mean it should be.

For example, there are ~130 lines of CSS in the Go file. Most editors have auto-formatting and suggestions for CSS for files ending in .css, which you won't get with this setup.

Also, with 885 lines of code, you have to wonder if reading the code is easier via scrolling / searching a single file or via tabs with multiple files.

(comment deleted)
How do I see the HTML and css in the GO code? Is it inlined somewhere ?