Show HN: I built a web framework in C
353 points
21 hours ago
| 44 comments
| github.com
| HN
jll29
3 minutes ago
[-]
Thanks for sharing; small is beautiful. A couple of points of feedback:

- check return value from malloc();

- consider using your own arena allocator (which gets a larger block of memory with a one-time call of malloc, then calls an in-process allocator that assigns part of that block);

- use a library prefix e.g. Lavandula_ before API functions like get() or runApp() to avoid name collisions.

- The JSON function is not spec-compliant; why not use an existing library? (I understand external dependecies may introduce unwanted bloat, but in this case, there are many compact and efficient options.)

reply
faxmeyourcode
19 hours ago
[-]
This is some of the cleanest, modern looking, beautiful C code I've seen in a while. I know it's not the kernel, and there's probably good reasons for lots of #ifdef conditionals, random underscored types, etc in bigger projects, but this is actually a great learning piece to teach folks the beauty of C.

I've also never seen tests written this way in C. Great work.

C was the first programming language I learned when I was still in middle/high school, raising the family PC out of the grave by installing free software - which I learned was mostly built in C. I never had many options for coursework in compsci until I was in college, where we did data structures and algorithms in C++, so I had a leg up as I'd already understood pointers. :-)

Happy to see C appreciated for what it is, a very clean and nice/simple language if you stay away from some of the nuts and bolts. Of course, the accessibility of the underlying nuts and bolts is one of the reasons for using C, so there's a balance.

reply
citizenpaul
18 hours ago
[-]
> I'd already understood pointers.

Ok I hear this all the time. Are pointers really that hard for so many people to understand? I'm not trying to brag it took me I think like 15 minutes to grok them from learning about them the first time. I'm sure it took me longer to be proficient but I don't get this legendary difficulty aura that seems to surround their existance.

Also yes nice project.

Job app complete projected archived and abandoned in 3...2..1... :). I hope not.

reply
ActorNightly
10 hours ago
[-]
The issue with pointers is that CS gets taught in a VERY bad way. The way it should be taught is starting with basic assembly on a microprocessor. This trains your brain to think of memory locations and data in memory.

Then when you start using pointers, it makes sense. If variable is a pointer, that means its a memory location. *variable is a way to get that data. Then arrays is just a wrapper around pointer arithmetic.

Whereas with CS, you learn about variables first, which is an abstraction on top of memory, and pointers don't make sense in this regard.

This is why any EE/ECE grads are much better developers than CS grads, because once you understand fundamentals

reply
hunter-gatherer
8 hours ago
[-]
Springer press has a book "Programming for Engineers and Scientists" or something like that, which is the first book I picked up to "self teach CS". From the get go pointers are involved and explained in this linear memory model and explained how they work on the stack and what not. I always thought this was the best approach; the reality is taught first, the abstraction (syntax) second. Not sure why so many programming books do it the other way.
reply
rockemsockem
7 hours ago
[-]
> This is why any EE/ECE grads are much better developers than CS grads, because once you understand fundamentals

This is largely not the case in my experience. They probably understand the lower level details of manipulating memory better, but there's a lot more to developing software than understanding that memory is a specific place.

reply
Jataman606
2 hours ago
[-]
> The issue with pointers is that CS gets taught in a VERY bad way. The way it should be taught is starting with basic assembly on a microprocessor. This trains your brain to think of memory locations and data in memory.

Can't agree with this enough. The moment i finally understood what pointers are was when I landed embedded job and during debugging session I looked at memory browser that showed my variable at exact address in memory. After that all about pointer arithmetic and even function pointers became clear as day. Something at least 3 teachers weren't able to explain clear enough.

reply
frabert
49 minutes ago
[-]
You can do the same with regular, non-embedded programs in a debugging session
reply
messe
5 hours ago
[-]
> This is why any EE/ECE grads are much better developers than CS grads, because once you understand fundamentals

Hah, like fuck they are. The worst code I regularly have to review is written by EE grads. They have less of an understanding of pointers than the people in the office with a CS background.

reply
noufalibrahim
17 hours ago
[-]
It's a rabbithole. Pointer to array of structures that have pointer fields. Array of pointers to structures etc. You pass them around and trip over the passing semantics, uninitialised pointers etc etc.
reply
citizenpaul
17 hours ago
[-]
Hmm. Perhaps I've just never encountered a hairy enough situation with them? That's what the eternal thought tracker notepad on my desk is for though. Maybe people are trying to do it all in their head? Pen and paper are too old school for the cool new 1000x devs?

I still feel like this argument could be transferred to nearly any concept in CS though. Abstract enough anywhere and you will always start exceeding the brains working memory.

reply
jacquesm
9 hours ago
[-]
A simple properly implemented doubly linked list or circular buffer is already above the level of most beginner C programmers. Though they're great exercises.
reply
jcelerier
16 hours ago
[-]
I don't think I would be comfortable saying I understand something if I'm not able to get it 100% clearly just from my thoughts and re-explain it to someone
reply
6510
11 hours ago
[-]
Everything is just numbers, then we pretend they are arrays, pointers, objects, classes, floats, websites, applications, certificates etc.. The imaginary array can really only contain numbers but we can pretend the numbers are other things, unicorns, rainbows, laser unicorns etc

We are just pretending, there is nothing to understand?

reply
moron4hire
4 hours ago
[-]
To take this a step further.

They aren't even numbers. They're voltage-high and voltage-low signals.

Numbers don't even exist! You'll never find a 2 in nature. You'll find two things, but you'll never find the 2 itself.

And all 2s are the same 2. But every voltage signal representing a 2 is a completely different voltage signal. Sometimes they aren't even voltage signals! Sometimes they're magnetic flux signals! Sometimes they're electrical field signals! Sometimes they're photons modulated down a wire made of glass!

But the 2 they represent? Not even that is 2. It's 10!

reply
diegof79
9 hours ago
[-]
Understanding the concept is easy.

The problem arises when you start to mix memory management with more complex structures.

It’s extremely easy to make mistakes, and you must be very careful about ownership and clean up. Those things are not strictly related to pointers, but in C, it’s inevitable to use pointers to handle them. That's why people say pointers are hard.

reply
moron4hire
4 hours ago
[-]
Yeah, it's one thing to understand "a pointer is a location in memory". It's a completely different things to understand what to do with that information.

When I first started learning to program, it was in C, with one of those "Sam's Learn Yerself a C++ in Eleventy-Three Days" books. I was, like, 15 or something. This was long enough ago and my family was just barely poor enough that we didn't even have the Internet yet.

The book explained memory. That was not hard to understand. But we had been using stack-allocated variables through several chapters in the book so far. I didn't get why you would ever want anything as a pointer. If I wanted to write a program to add 3 and 5, why wouldn't I just say "int x = 3;"? Why bother with this stupid dereferencing syntax? Given that the author chose to explain pointers by first explain the address-of operator on stack allocated variables, it felt particularly perverse. The regular, ol' variables were right there! Why but just use them

I didn't have a concept yet of what one could even do with programming. Hell, just a few years prior to that point, I was pretty sure all of the other programs on my computer were written by massive teams of wizards manually typing out 1s and 0s.

I still managed to bungle on and write code. But my programs in C never really worked well. Though, they still worked better than my classmates' versions! Then, in my 2nd year of college, I had transferred universities and the new place taught in a Java.

Java was disgusting. It was so verbose. Why did we need all these weird, multi-sylabic, period-infested function calls to do anything? Why was it so slow? Why couldn't I write ASCII-art graphics with it? Why couldn't I run my programs on my friend's computer?

It wasn't until I had taken computer architecture that I gained a much better understanding of what any of all these computer things were meant to do. I ended up implementing a basic scripting language in Java. And then, suddenly, I understood pointers.

reply
lelanthran
15 hours ago
[-]
> Are pointers really that hard for so many people to understand?

Apparently they are; I believe it's the indirection that gets people.

Most learners aren't really taught basics properly - they learn that a variable "contains" a value, when instead they should learn that all values have a type, and some variables hold values.

> I'm not trying to brag it took me I think like 15 minutes to grok them from learning about them the first time.

I can't remember not knowing pointers, so I can't really tell you how long it took for it to click, but I do know that I had done a non-trivial amount of assembly before I used C, so maybe that helped/.

reply
supportengineer
12 hours ago
[-]
Pointers make perfect sense.

Now dependency injection, that's some magical bullshit right there.

reply
munchlax
1 hour ago
[-]
If you have a class whose constructor depends on an instance of something else, you can instantiate that first and pass it on.

That's all there's to it.

You can do DI in your own startup code and have some logic in there that substitutes mocks when running under test. Or you could change the logging when debug is enabled. Hardly rocket science. If you can write code, you can write the startup code.

If your team likes patterns, dont mention dependency injection unless you're confident it wont get replaced with the framework of the day.

See https://www.jamesshore.com/v2/blog/2023/the-problem-with-dep...

Frameworks turn your DI code into highly complicated configuration. The end result is a giant lump of code whose only achievement is hiding the new operator and possibly also someones job security.

reply
ranger_danger
14 hours ago
[-]
I think it would help a lot if pointers were taught to people from the perspective of how they actually occupy memory, and what the value it stores in memory represents, and then how that value is an address that is followed when a pointer is "dereferenced", etc.

It seems a lot of people assume that pointers don't actually consume any memory and then get confused trying to understand it that way.

reply
EvanAnderson
14 hours ago
[-]
Strong agreement.

I came at C after doing 6502 and 8086 assembler. Pointers just made sense because working with indirect addressing and understanding how things were stored in memory already made sense.

reply
bionsystem
11 hours ago
[-]
The way a lot of teachers teach it is plain trash. I was raking terrible grades in OCaml and C before a student showed me what I needed to know in 15 minutes, and then I would kill it in the remaining exams. Same thing happened to my 68000 course. It didn't happen with every teacher but still, some people really need to get some better pedagogy.
reply
jug
11 hours ago
[-]
The worst thing with C pointers was for me that the asterisk is inexplicably used both to declare a pointer and a COMPLETELY different operation of dereferencing a pointer.

I still don't understand this decision. I think it should've been like int^ p = &i; ... or ... int i = *p;

Everything clicked ironically when I went even deeper and studied assembly language. Then following pointers to data vs just reading pointers becomes very clear and explicit.

reply
sgsjchs
9 hours ago
[-]
> I still don't understand this decision.

Variable declaration `T v;` means "declare `v` such that expression `v` has type `T`". Variable declaration `T *p` means declare `p` such that the expression `*p` has type `T`". etc.

reply
kopirgan
6 hours ago
[-]
Nice explanation!
reply
tealpod
2 hours ago
[-]
> asterisk is inexplicably used both to declare a pointer and a COMPLETELY different operation of dereferencing a pointer.

This is the most confusing concept of pointers. I feel this could have been easily avoided with different character like ~ or ^ or other.

reply
resonious
2 hours ago
[-]
As a 16 year old who learned programming by hacking together bad ActionScript3 flash games, it took me way longer than 15 minutes. Once I got it I got it and there was no mystery from then on. But there definitely was a hurdle.
reply
DanielHB
15 hours ago
[-]
> Are pointers really that hard for so many people to understand?

Yes, anyone who has taken algorithms and data structures class in C knows that some people just don't get it.

Also the way people teach it tends to be bad, before teaching pointers you need to teach Stack and Heap at a conceptual level.

reply
codegeek
14 hours ago
[-]
"Are pointers really that hard for so many people to understand?"

The * vs & always gets me and not to mention if I ever have to deal with Pointer Math.

reply
bionsystem
11 hours ago
[-]
Think of it as types. All of the following are the same thing (declare p as an int* type). It's important for the end :

  int * p;
  int *p;
  int* p;
Now remember that the type is a memory address. I'm sure it is semantically wrong for whatever reason somebody will explain but it helps to think about it. So you can do :

  int my_number = 6;
  int* p = &my_number;
Both sides of the "=" are the same type (int* is an address, and &my_number is also an address, the one of my_number).

Now p is a pointer (or an int* or an address), and *p is... an int ! So this is totally valid :

  printf("%d\n", *p)
and for anything else than int you need to malloc that so you will see a lot of :

  my_struct* s = malloc(sizeof(my_struct);
which makes sense because malloc returns an address (the beginning address of the content of s ; yet again somebody will tell me I'm wrong to call it "the content of s" so sorry for that).

  my_struct* // is the type of s, it is an address
  my_struct // is the type of *s (some custom type of size sizeof(my_struct))
reply
1718627440
11 hours ago
[-]
> int* p

I don't like that syntax, because it confuses people. It might be sensible to think of the type as (int *), but C just doesn't work this way. You might never declare more that a single variable in a statement, but it still gives people the wrong intuition.

reply
usefulcat
5 hours ago
[-]
> int* p

I very much prefer that syntax, because the '*' is part of the type, not part of the variable name.

> You might never declare more that a single variable in a statement

    int a, *b, c, *d;
Yes, you can do that, and in fact if you want to declare multiple pointers on the same line, you are required to put a '*' in front of every pointer variable name.

Personally, I've always considered this to be a defect of the language. Would it really be so awful to have to write instead:

    // Both are of type 'int'. Pretty straightforward.
    int  a, c;
    // In my fantasy world, both of these would be of type 'pointer to int',
    // but IRL b is a pointer and d is an int. fun!
    int* b, d;
But of course that's not the language we have.

I'd be very curious to know the motivation for requiring a '*' in front of each pointer variable name, as opposed to once with the type. So far the only ones I've thought of (not mutually exclusive) are:

a) we REALLY love terseness, and

b) ooh look at this clever hack I came up with for variable declaration syntax.

reply
Hunpeter
57 minutes ago
[-]
Someone said in another comment, that it makes more sense together with the dereference operator, so int *var means: "dereferencing var gets you an int."

I don't really know C, but personally prefer your version. However, I can also get behind considering the * to be part of the variable, rather than a type: "var is a pointer that happens to hold an int". I mean, maybe they could have used the & operator meaning "var is an address holding an int"? Honestly, it just feels like there's too much tradition and legacy and so on, and what one considers intuitive is the thing that they're most used to.

reply
TZubiri
14 hours ago
[-]
*pointer = what pointer points to &thing = address of thing
reply
faxmeyourcode
15 hours ago
[-]
Pointers in and of themselves are not difficult to learn on their own, but when you're learning them alongside your first programming language, it's just adds to the difficulty I think.

I think a lot of noobs learning C struggle with pointers especially because there are no good error messages besides "segmentation fault" :D

reply
copperx
17 hours ago
[-]
The concept is straightforward. The syntax isn't. That's why cdecl.org exists.
reply
citizenpaul
17 hours ago
[-]
C gibberish to English gave me a chuckle thanks.
reply
wkjagt
16 hours ago
[-]
I understood them on a superficial level when first learning about them, but it only really clicked after having done assembly.
reply
dingdingdang
15 hours ago
[-]
The legendary status is also enhanced by the absolute nightmare that pointers enable if used with indiscretion or high level proficiency - a triple pointer is a good example for me but there's many many more, and arguably worse, examples out there.
reply
koolba
7 hours ago
[-]
My favorite is pointers to functions passed as args for things like supplying a comparator. With more opaque void* args for customizing things even further.
reply
assimpleaspossi
17 hours ago
[-]
Same here about pointers. Perhaps it's cause I started life as an electronic engineer and understood memory addressing from the chip level but I, too, don't understand the struggle others seem to have.
reply
citizenpaul
17 hours ago
[-]
I started in networking so there were a lot of memory/bit-logic/binary concepts piled on early maybe that is the case.
reply
leptons
16 hours ago
[-]
I learned assembly language long before I learned C, so pointers took me about 2 seconds to understand. I suppose it may depend on previous experience.
reply
bsder
9 hours ago
[-]
> Are pointers really that hard for so many people to understand?

Yes. Especially pointer to pointer to ...

The big problem is that arrays are conflated with pointers because C doesn't do slices. If C had slices, people would naturally avoid pointers except in the cases where they were genuinely necessary. That would make teaching pointers vastly easier.

reply
ashtonjamesd
19 hours ago
[-]
Wow! That really means a lot because I always make a lot of effort to make sure my code is just that :)

Appreciate you saying that!

reply
jacquesm
18 hours ago
[-]
You've done a couple of things right: very few dependencies, simple, easy to understand code. C gets hairy when you try to be clever.

I'm busy writing some of the most optimized-but-still-portable code that I've ever written and it is very interesting to see how even a slight difference in how you express something can cause a massive difference in execution speed (especially, obviously, in inner loops). Your code is clearly written from what your comfort zone with C is and I'm really impressed by the restraint on display. At the same time, some of the code feels a bit repetitive and would benefit from more universal mechanisms. But that would require more effort and I'm not even sure if that is productive. One part where I see this is in the argument parsing code as well as in the way you handle strings, it is all coded very explicitly, which substantially increases the chance of making a mistake.

Another limitation is that using AI to help you write the code means you don't actually understand what it does, and this in turn may expose you to side effects that you are not able to eliminate because you did not consider them while writing, it is as if someone else gave you that code and asked you to trust them they did not make any mistakes.

reply
codegeek
19 hours ago
[-]
People, stop trying to be so serious and nitpick this project. This is a great example of an actual HN worthy share. Someone built a cool project and explored the possibilities with C. This is not something we need to analyze with "oh can it replace PHP" etc.

Good job OP. Now if you can add HTML templating, this may become a complete framework :)

reply
ashtonjamesd
18 hours ago
[-]
Thank you, I really appreciate you saying that!

Yes it's on the backlog and will be fun to implement :)

reply
whatamidoingyo
11 hours ago
[-]
As someone learning C for fun, I agree. This project is awesome!
reply
hgs3
19 hours ago
[-]
The code is very readable and well organized. My only major critique is that there's very little error checking, e.g. there are many calls to snprintf and malloc without checking the result. There is also an unused loop here [1].

As an aside, I don't see any support for parallelization. That's fine for an initial implementation, but web servers do benefit from threading off requests. If you go that route (pun intended) you might consider using something like libuv [2].

[1] https://github.com/ashtonjamesd/lavandula/blob/51d86a284dc7d...

[2] https://github.com/libuv/libuv

reply
ashtonjamesd
18 hours ago
[-]
Thank you for the feedback, it is appreciated!

I did intend to implement parallelization as a later feature so it's good to bring it up.

reply
sroerick
19 hours ago
[-]
Hi, I think this is great. I've really enjoyed working with Jetzig, which is sort of similar.

I also love the BSD C CGI Postgres stack. I'm just a CRUDmonkey with mostly python skills, so getting to explore low language and memory concepts is a lot of fun for me.

People will whine and moan about how this is not practical, but as embedded devices become more ubiquitous I think a clear value add may actually emerge.

I've been playing with the pico calc, and if I was building something as a "mobile app" for that I would much rather reach for C for my framework code.

Cheers, great work

reply
lubesGordi
19 hours ago
[-]
Well I don't know about others here, but I think its cool. If you can make the setup super readable and get the performance of C then why not? Especially now when you can get claude to write a bunch of the framework for you. Add in whatever you need whenever you need it and you automatically have a platform independent web framework that's no bigger than what you need and likely decently performant.
reply
maybewhenthesun
19 hours ago
[-]
Maintainer nightmare checklist:

- Web framework : inherently hard to maintain due to communication over evolving standards. Check.

- AI written code where nobody knows howwhatwhenwhy!? Check.

- Written in C. Check.

bwahahahaha!

edit: semi-joking. As I actually like the simplicity of pure C. But the combination of AI written,network-facing and C makes me shudder.

reply
ashtonjamesd
19 hours ago
[-]
Haha, I have used AI in some parts of it - mainly the JSON part because I could not wrap my head around it for the life of me. But I am proud that 90% is self written!
reply
jvanderbot
19 hours ago
[-]
That is excellent. Well done.
reply
jvanderbot
19 hours ago
[-]
I think the old HN ethos that I loved, on full display here, won't survive intact in the AI era. It'll have to change from "It is cool to try making <neat tool> in <non obvious language>". Such a project is now a prompt away, and there's light-years of distance between a carefully hand crafted version and something that is posted aspirationally by an AI.

Every agent I know of or use will always say they built "Production ready, secure, fast package for X" if you ask them to build that, but they rarely actually will. It takes enormous time and effort to actually do that, and any first iteration of "production ready" is definitely aspirational until it actually hits the real world and survives. I'm speaking from experience, fwiw.

reply
faichai
5 hours ago
[-]
Some unsolicited feedback:

I think the appRoute macro obfuscates the types and signatures, and introduces some unnecessary indirection. I would get rid of it.

Related, the AppContext type could be renamed RequestContext or ControllerContext or something as its App + HTTP Request + DB and not just the App.

Otherwise, I agree with other commenters that this is some of the cleanest C code I’ve seen in a while! Great effort!

reply
mistivia
14 hours ago
[-]
It's very dangerous to write a http parser from scratch in C. This can be very vulnerable without rigorous testing. To get a useful web framework for production in C, I think it's a better idea to start from libmicrohttpd, libevent_http, or even fastcgi, which are battle-tested.
reply
wallmountedtv
5 hours ago
[-]
I hear this comment warnings, and can easily see this myself being true. But, how could one actually make a reasonably safe http server in C from scratch?

That would honestly sound like an amazing book, just walking through all the ways it's horrible chapter by chapter, and how to structure the code instead, slowly. Like an accelerated history to create such a matured http library.

reply
jacquesm
9 hours ago
[-]
I don't think anybody here is going to use this for production, but just in case you're tempted: don't.
reply
levkk
19 hours ago
[-]
That's awesome. With macros, you can go far and most modern web frameworks use whatever complex tools their language allows (like metaprogramming in Rails).

Mad props for building this. It's hard and it's fun!

As to other comments in the thread about the "why": why not. For the love of the craft.

reply
ashtonjamesd
19 hours ago
[-]
Thank you so much! I appreciate it :) And yes, totally agree.
reply
fallingmeat
19 hours ago
[-]
wow that’s a lot of HATE for a really well organized project with some great ideas. Killer job Ashton, you just built some skills they can’t take away from you.
reply
ashtonjamesd
19 hours ago
[-]
Thank you, that means a lot! :)
reply
koito17
12 hours ago
[-]
The README gets straight to the point and I really like that.

Additionally, the .env file parser is quite clean.

https://github.com/ashtonjamesd/lavandula/blob/main/src/dote...

However, it doesn't seem that the parser supports comments. I guess a "good first issue" for anyone interested in contributing would be extending the `skipWhitespace` function to detect `#` tokens and skip the rest of the line when present.

Would also need to handle edge cases like env vars having values containing `#` tokens inside (but these will be quoted, so it's probably not too tricky to handle.)

reply
jpc0
1 hour ago
[-]
So what happens when the env value happens to actually have a # in it?

So you then need to implement escaping which can go from a very simple implementation to an actual lookahead parser

EDIT:

Actually I agree, this parser is already very overbuilt and should be able to handle comments. Generally an env parser is a few lines a best… you need to read a line, look for the first instance of the separator, generally no reason to build a full parser for that, env is an absurdly simple format if you don’t want features like comments.

Hell even an ini format parser is simple to implement in the same style.

reply
koolba
7 hours ago
[-]
Why is there an env file parser at all?
reply
freetonik
19 hours ago
[-]
I hope you don't feel discouraged by some comments questioning the meaningfulness of this. It's a cool project, and you obviously put some thought into it. Congrats!
reply
freetonik
19 hours ago
[-]
In addition, OP clearly describes themselves as a "Fanatical C Developer", so that's enough justification in my book! :)
reply
Teknomadix
19 hours ago
[-]
Doing what you love is fully justified in 2025.
reply
ashtonjamesd
19 hours ago
[-]
No of course not, I understand where they are coming from in all honesty. Thank you that means a lot!
reply
dang
16 hours ago
[-]
Offtopic (sorry) but this thread is such a good example of the contrarian dynamic that I can't resist!

The "contrarian dynamic" (https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...) is the tendency for reflexively negative comments to show up early with shallow/generic objections to a submission, followed by a later wave of comments objecting to the objections and defending the submission.

The latter tend to get upvoted—rightly so, since they are more positive and usually more substantive. This puts the thread in the paradoxical-but-common state where the top comments are objecting to how prominent the bottom comments are! (Or, rather, were.) That's odd, but at least it's better than having the negative ones at the top.

In this case, these 5 comments all appear higher in the thread:

https://news.ycombinator.com/item?id=45528218

https://news.ycombinator.com/item?id=45527967

https://news.ycombinator.com/item?id=45527886

https://news.ycombinator.com/item?id=45527879

https://news.ycombinator.com/item?id=45527728

... than the negative(ish) ones that were posted earlier:

https://news.ycombinator.com/item?id=45527887

https://news.ycombinator.com/item?id=45527480

https://news.ycombinator.com/item?id=45527387

https://news.ycombinator.com/item?id=45527278

https://news.ycombinator.com/item?id=45527259

Some of those were only slightly negative and probably not meant that way, but yeah, the early impact of running into a bunch of these leads to a WTF feeling.

Ultimately I think this has to do with the reflexive/reflective distinction: https://hn.algolia.com/?dateRange=all&page=0&prefix=true&sor.... That's probably the clearest way of describing the difference between the kind of comments we want on this site vs. the kind we don't want.

reply
pierrec
9 hours ago
[-]
"That's odd, but at least it's better than having the negative ones at the top."

You're being too kind, but I suppose your work requires diplomacy. When multiple top-level comments basically say "I can't believe the amount of hate in this thread", without adding any other thought, the thread start to feel like a simulacra of a discussion, an absurd comedy of nonsense, a deeply layered shaggy dog story.

Come on, people. Want to counterbalance negative comments? Find something positive and interesting to say. Yes, this will require actual effort, as with many things in life. You can do it!

reply
lebimas
7 hours ago
[-]
Real question, how did you learn how to code this well? I found your LinkedIn from your Github, and as someone who is just committing to becoming a SWE at 26, having learned a bit of Python and Matlab in college, and a bit of Java in high school, yet never fully grasped it and thus avoided it for as long as possible, I'm impressed by people who have this caliber of abilities at such a young age. Are there any tips or bits of advice you (or anyone else on HN for that matter) would give to someone who really wants to be the best they can possibly be at coding?
reply
ashtonjamesd
1 hour ago
[-]
Hi, I have been coding on GitHub for about 2 years. There's no trick other than enjoying it and relentlessly programming for fun. You probably know this, but if you enjoy something, you'll likely be better at it than someone who doesn't! More concrete advice would be to become an expert on the fundamentals and then try to tackle large projects, things that you think you could definitely not do, but do them anyway.
reply
p0w3n3d
19 hours ago
[-]
Great work! Thank you! That's what I've been looking for for a long time.

Still probably I'm going to continue learning golang in most situations, because that's where the money is (i.e. job offers), but I will create a hobby project based on your framework.

--- EDIT ---

> 5 hours ago

Ohh it's fresh. I almost smell the freshly baked buns with my mind

reply
ashtonjamesd
19 hours ago
[-]
That's amazing to hear and motivates me to solidify the framework further. I appreciate you showing interest! :)

I'd love to hear about your project when you get round to it.

reply
elevation
18 hours ago
[-]
I have considered porting a couple production apps from python to C; at this stage in their lifecycle they would benefit more from C's execution speed than from python's development speed.

Your work is a nice reference, it is neat to see someone else working in this space!

reply
jimbokun
15 hours ago
[-]
Would Go be a happy middle ground?

The translation should be much faster while giving a lot of the efficiency benefits of C (but by no means all).

reply
201984
9 hours ago
[-]
Very nice!

A couple of notes: you'll want to use non-blocking I/O and an event loop to prevent one slow client from locking up the whole server. You should also check for partial read and write calls, so that if a client sends a couple bytes at a time, you can buffer up their full response and still be able to respond to it. A fixed size buffer for requests isn't ideal either since POST requests can easily blow through your 4096 byte buffer.

You might also want to look into using an AF_INET6 socket. You can still accept IPv4 connections, but you'll also gain IPv6 basically for free, and in 2025, you really should support IPv6.

reply
ak39
2 hours ago
[-]
Nice.

Is if a one thread per request model?

Does it support async await type of architecture?

reply
coreyp_1
19 hours ago
[-]
I'm wanting to do the same thing. I've also already written a language (in C) to generate HTML (a template language), so these two go hand-in-hand!
reply
OneLessThing
9 hours ago
[-]
There is a heap overflow in the http parser. Should I spoil it or let people find it on their own?
reply
OneLessThing
6 hours ago
[-]
Heres a link to the the problem I found: https://alew.is/lava.html
reply
dboon
18 hours ago
[-]
C is really, really ripe for tooling and modern libraries. There are a lot of great ones already that don’t resemble what I’ll call university C in the slightest (i.e. the C most of us remember writing; awful, bug filled, segfaulting)

I’ve been building out my C standard library replacement in earnest for a little while. If you like this framework, check it out.

https://github.com/tspader/sp

reply
jacquesm
19 hours ago
[-]
If you're going to use local allocation of short lived buffers then don't use malloc but use alloca. That's much cleaner.

http.c around line 398, that looks wrong.

reply
201984
9 hours ago
[-]
Arenas would be a better fit for a webserver. Allocations are basically free and everything gets deallocated all at once, so having one arena per active request and resetting it between requests can give very high performance.

Using alloca will result in stack overflows if you use more than a couple megabytes, so it isn't a very good idea.

reply
jacquesm
9 hours ago
[-]
Those buffers are tiny and they won't result in stack overflows because the whole thing is single threaded, it is beginner level code.
reply
gpm
17 hours ago
[-]
I've been told that modern compilers really don't like alloca, is that wrong?
reply
jacquesm
17 hours ago
[-]
I don't know who told you. But it's a lot slower than malloc, and requires you to do a bunch of bookkeeping, which is easy to mess up if you have multiple exits from your function.
reply
CyberDildonics
9 hours ago
[-]
alloca is just a couple of instructions to allocate more memory on the stack, it is much faster than malloc for pretty much every reason including locality and the fact that it doesn't have to be freed because it goes away after the current scope.
reply
z3ratul163071
3 hours ago
[-]
awesome!

hope hw vendors will adopt it so their management web pages are less ass than they actually are currently.

reply
OutputRiff
19 hours ago
[-]
The repo looks fantastic! I'd love to see a demo and didn't seen one readily available in the readme.

I had such a bad experience with GWT back in the Java days of my life that I've steered clear of any "server" language for web frameworks since. I'd love for that to change though. I definitely will be trying this out.

reply
sim7c00
18 hours ago
[-]
really nicely written. inrespect this is maybe known / unneeded comment, but why bother with basic auth at all, especially when there is no TLS?

i understand other auth schemes are more complicated, and maybe theres no desire to pull in big libraries. just that if theres no TLS or proper auth, you can also just skip basic auth. its only use would be to trick someone who's not familiar (unlikely with such a repo but not impossible) into a false sense of security.

ofc, not really an issue with the code, and its an excellent base to look into how this stuff works and if you want since its pretty clean and easy to ready, expand upon it. well done! love ppl churning out good ol C projects. respect!

reply
sweetjuly
16 hours ago
[-]
It's fairly common to use something like nginx as a forward proxy and do TLS there. IPv4 and NAT makes this essentially mandatory if you want to host multiple services due to eSNI. You wouldn't necessarily have protection inside the server network (which isn't great) but you at least get protection everywhere else.
reply
severino
17 hours ago
[-]
> why bother with basic auth at all, especially when there is no TLS?

Maybe to have some "basic" auth for an embedded device web interface or something like that? I suppose it's better than nothing. I've devices which prompt for username and password with no TLS either.

reply
kjs3
15 hours ago
[-]
Basic auth can keep the crawlers out, for one thing.
reply
orochimaaru
17 hours ago
[-]
This is very cool. I may take the same concepts you have and do this in rust and zig for fun and learning.

Yeah, I know those languages have a the frameworks but nothing really beats understanding something like doing it ground up on your own.

reply
elevation
18 hours ago
[-]
Nice work! I like the little test framework you built. Have you considered making runTest a macro so that you can print the name of the test along with the test result?
reply
ashtonjamesd
18 hours ago
[-]
That's a very good idea actually and I had wanted to do that but it didn't click that you could do that with a macro!

Thank you, I'll will implement that :)

reply
elevation
18 hours ago
[-]
For the ultimate in readable test reports, you can prettify the test name by:

* dropping the prefix "test_" * substituting the "_" characters in the function for whitespace * uppercasing the first letter of each word.

So `test_tokenize_simple_model` becomes "Tokenize Simple Model".

reply
jcmontx
18 hours ago
[-]
I often forget how similar to Golang C looks and feels
reply
krowek
15 hours ago
[-]
Curious, why did you decide to go with your own test helpers rather than using something like check?
reply
Maksadbek
14 hours ago
[-]
Couldn't believe my eyes, this is the cleanest C code I've ever seen!!
reply
SvenL
15 hours ago
[-]
I like it. I was looking for something like this and I will take a look into it.
reply
capestart
19 hours ago
[-]
This looks cool
reply
dariosalvi78
18 hours ago
[-]
How compatible is this with embedded devices? How much does this depend on OS APIs?
reply
kahlonel
19 hours ago
[-]
That's a great example of how to write C in 2025. Congrats and well done.
reply
BiraIgnacio
10 hours ago
[-]
Great work, thanks for this!
reply
gwbas1c
20 hours ago
[-]
I don't understand the example. Does it even compile?

It's been a long time since I've used C, so maybe it's using some syntax that I'm unaware of?

IE: What defines "home" that is referenced as an argument to the "appRoute" function, and then passed to the "get" function to set up the "/home" route? Is "home" defined in lavandula.h, or is this really pseudocode?

reply
ashtonjamesd
20 hours ago
[-]
Hi, sorry maybe I should've added a comment for that.

The 'appRoute' is a macro that expands to a function signature.

The macro is: '#define appRoute(name) HttpResponse name(AppContext ctx)' and the parameter I passed as 'home' is expanded into the function name. The reason is because all controller function signatures are the same, so just writing 'appRoute' allows the developer to save time writing endpoints!

It is a tradeoff between readability and development speed. And one of the ideas behind the framework is succint and minimal code.

reply
gwbas1c
19 hours ago
[-]
So it creates a function called "home", and that is what you pass to get?

Makes sense, thanks!

reply
nodesocket
16 hours ago
[-]
Thanks for explaining. I was also a bit confused at a first where the home variable being passed into get() was coming from.
reply
hofrogs
20 hours ago
[-]
"appRoute(home)" is a macro that expands to a function called "home":

    #define appRoute(name) HttpResponse name(AppContext ctx)
reply
rnhmjoj
20 hours ago
[-]
If I can guess, I would say `appRoute` is a macro that defines a struct called `home` with that handler being assigned to some field as a function pointer.
reply
diath
20 hours ago
[-]
It's a macro:

    #define appRoute(name) HttpResponse name(AppContext ctx)
reply
defraudbah
19 hours ago
[-]
github is giving me 503, the project is too good for mS

Thanks for sharing, this looks amazing

reply
leptons
16 hours ago
[-]
Does it do HTTPS? I'd be interested to try it on ESP32 but it has to support HTTPS.
reply
globalnode
19 hours ago
[-]
I like this, thanks for sharing. I recently did some work with a python web server using the basehttpserver and it was amazingly easy. Pythons even got built in tls support, would that be doable in your server? Its not that necessary with reverse proxies but its still nice for hobby projects.
reply
ashtonjamesd
19 hours ago
[-]
Yes, I'm sure that is something I can add to it.

I will add it to the backlog of things to do :)

reply
ranger_danger
14 hours ago
[-]
which version of C does this conform to?
reply
cyberax
15 hours ago
[-]
Uhh... This is an example why C is so bad for network-facing stuff:

https://github.com/ashtonjamesd/lavandula/blob/2dbefe6da16bf... - is it intended?

https://github.com/ashtonjamesd/lavandula/blob/2dbefe6da16bf... - pain....

reply
badsectoracula
14 hours ago
[-]
I get the first one but what is the issue with the second one? It looks like a fairly standard dynamic array with separate size and capacity.
reply
jacquesm
8 hours ago
[-]
Missing null pointer check on return, leaks memory because it overwrites the original pointer.
reply
cyberax
2 hours ago
[-]
No null pointer check, and the fact that you have to manually rewrite it every time.
reply
EGreg
20 hours ago
[-]
Take a look at https://www.reddit.com/r/programming/comments/225ovy/okws_ok...

This was years ago (20 years ago?)

reply
guerrilla
20 hours ago
[-]
For fun or why?
reply
ashtonjamesd
20 hours ago
[-]
For fun! And because I wanted to create a framework that makes coding in C feel like a high level language (mainly for fun though).
reply
hmry
20 hours ago
[-]
Science isn't about why, it's about why not.
reply
cozzyd
20 hours ago
[-]
I think it makes lots of sense when adding e.g. a live view to some C daemon running on a single board computer. Obviously in these cases you're not generally on the public Internet and your clients are trusted.
reply
JKCalhoun
20 hours ago
[-]
I'm stupid — is this to create web apps that run on the server? More or less replacing PHP or whatever?
reply
ashtonjamesd
20 hours ago
[-]
Right now, it's just a framework for building backends. So yes, server-side applications. However, I have thought about implementing a templating engine for serving HTML files.
reply
Hackbraten
19 hours ago
[-]
This can be super useful for IoT or embedded devices with web interfaces but restricted resources.
reply
sixtyj
20 hours ago
[-]
Edit: I am considering to delete the following paragraph as it seems that my hands were quicker than my brain :)

I'm sorry, but it's like scratching your left ear with your right hand. But for fun, yeah, there are worse things people do. Good luck and have fun. Now here's where most of us will probably be sarcastic, but it's certainly a good way to explore whatever others consider bullshit.

Edit: Pls read the following comment. I would hire him/her because I consider this as a waste of OP skills and he/she would be useful in many more projects.

TLDR; it was not a hate. I am sorry if it sounds so.

reply
ashtonjamesd
20 hours ago
[-]
True. Also, I love the C language and I don't get joy out of writing in many other languages. Additionally, I've wanted to make something like this just to learn more about how web servers work. I appreciate your thoughts.
reply
sim7c00
18 hours ago
[-]
love this sentiment. i cant really write in other languages. i try but always C ends up the choice :'). slow going but happy going
reply
sixtyj
19 hours ago
[-]
It is not about threat. It is about that life is too short to do things that are almost nonsense. Ofc everyone of us consider “nonsense” in different way.

I wish OP good luck. It was not sarcastic, I really do, and would like to hire him/her for the skills. But for mankind, this project is almost useless… I apologize if this sounds harsh.

reply
ashtonjamesd
19 hours ago
[-]
That's fair. And I do agree. The use cases for something like this are very thin compared to what tools you can use out there instead, Django, Rails, Express, etc. All of which offer a much safer development experience. However, I still believe it will have a use case for some.
reply
sixtyj
19 hours ago
[-]
You are right. But I really think if you know C lang (all of Python or PHP people could be jealous :) - you can easily focus on something a little bit important/useful that will have impact on humanity.

You have a great potential if you can “see code” and have logical thinking deep inside. Not too many people have it.

Elon Musk said once that all those innovations are redeemed by the tremendous efforts of all the engineers. So I appreciate everyone who can do something.

reply
seanssel
19 hours ago
[-]
Oh please, get off the weird high horse. I find this comment to be “nonsense”. What projects are you working on now, for mankind?
reply
sixtyj
19 hours ago
[-]
Why are we alive? To be useful. Not happy all the kind. Everyone wants to be useful.

I am not doing anything special but I do inform our community (“mankind”) for 25 years… And I feel useful because I am good at it.

“Mankind” can be a group of other people.

Edit: What people value the most? Compliments. So if you are useful and receive compliments, you will eventually be happy. But ofc you can be happy without being useful, for sure.

reply
kjs3
15 hours ago
[-]
I am not doing anything special

Least surprising thing you've said so far.

but I do inform our community

Inform them what? "You there, this project you've worked on, learned from, gotten joy from, been complimented on by your peers...I, with my 25 years of peerless wisdom, find it useless and you should feel bad for doing it"?

Sure...you're a real people person.

reply
chrsig
18 hours ago
[-]
to be useful to whom, exactly?
reply
sixtyj
18 hours ago
[-]
To other people, of course.
reply
jacquesm
18 hours ago
[-]
OP has done more to be useful to other people than you did in this particular thread. Ok, so what if it has been done many times before, this is his, it may not be perfect and it may not be immediately useful to you. But it increased his knowledge and he shipped, which is more than I can say for 95% of my own projects, so that's impressive by itself. He also opened himself up to criticism and takes it all in stride, which is another fairly scary but powerful thing to do.

Fun fact: I've built something very much like this that powered a number of programs that I sold over the years and it was written when I wasn't nearly as good of a programmer as I am now (take off 30 years of additional experience). If I look at OP's code there are a whole raft of nitpicks but there isn't anything immediately and obviously wrong with it and just speaking for myself, that is surprising because most people's C code is - and I'm being generous here - absolutely terrible. This has potential, but I'd have to really dig in to see how solid it is and I don't have time for that right now, but I've seen far worse code than this.

reply
kjs3
15 hours ago
[-]
It doesn't sound 'harsh'. It sounds 'condescending', especially when paired with a completely insincere "I apologize...".

It is about that life is too short to do things that are almost nonsense.

Jeez you must be a real joy to be around for the folks unfortunate enough to have to. /s

reply
password54321
19 hours ago
[-]
Why do web developers feel threatened that someone just built a web framework for fun?
reply
jermaustin1
19 hours ago
[-]
As a web developer who's first paid web site was in 1998 when I was 10-years-old, my favorite thing to do in my spare time is build web frameworks that I will never use.

- I've done CSS frameworks that replicate most of bootstrap that I use.

- I've made client-side reactive web-components (kind of) that almost replaced the parts of react that I like.

- I've built bespoke HTTP servers countless times since the VB6 days.

- And I've written my own MVC engines probably a half dozen times, just to learn a new language or library.

All of that to say, it isn't web devs who are threatened, it is developers who don't want to learn the underlying technologies that power the libraries and frameworks they use.

I actually see no fault in being that way. I've know tons of decent-to-good developers that have no desire to understand HTTP or Vanilla JavaScript, and they still do great work tying systems together. It's all about the kind of learner you are. Do you want depth, breadth, or a mixture of both (but always lacking in both - aka me).

reply
sixtyj
19 hours ago
[-]
That is what I wanted to say too… but I did it wrong way in previous comment.. oops
reply
jermaustin1
18 hours ago
[-]
We all trip up on our words sometimes. To err is human.
reply
jermaustin1
19 hours ago
[-]
An old boss of mine was an early developer for match.com, their entire web app was a monolithic C application, and, if I'm not mistaken, an ISCSI shared file-system based "database".
reply
sixtyj
19 hours ago
[-]
All big projects eventually have a specific background. I totally agree. Sometimes it works. Other times it doesn't work in long term and the cursed technology debt catches up with the company.
reply
yodon
20 hours ago
[-]
[flagged]
reply
johnisgood
20 hours ago
[-]
Use static analysis (Coverity, Coccinelle, sparse), enable KASAN/UBSAN, follow the SEI C Coding standard or MISRA C, and rely on the review process.

Many popular C projects do really well. Projects that you probably use.

Memory-safe languages eliminate vulnerability classes, but well-engineered C has proven viable for security-critical <insert whatever you want> infrastructure. The real question is whether the framework maintains that standard, not whether C is inherently unsuitable, thus the security concerns are legitimate but not absolute.

I think you are being a bit too dismissive, and your comment puts nothing concrete on the table.

reply
yodon
19 hours ago
[-]
You CAN write good code in any language. The issue is, as you say, that memory-safe languages eliminate entire vulnerability classes, vulnerability classes that are among the most trivially exploitable.

Can write safe code does not mean always writes safe code. A web server needs to be safe code, always.

reply
hu3
19 hours ago
[-]
This tired, flamewar-prone argument of gatekeeping new code in C/C++. Oh the irony coming from someone who wrote this some days ago:

> One of the highest priorities for the HN algorithm is to promote good interactions and discourage bad interactions. The logic is if you have a lot of people bickering with each other, regardless of the topic, it normalizes bad behavior. HN is trying to sustain itself as a forum with great discussions.

reply
yodon
19 hours ago
[-]
I notice you chose to attack me rather than attacking the assertion that memory-safe languages are inherently safer than memory-unsafe languages like C. Yes, you CAN write memory safe code in C. You DO write memory safe code in languages like Java, Python, PHP, and C#. Critically, the maintenance programmer also writes memory safe code when working in a memory safe language. The maintenance programmer is not guaranteed to write memory safe code when working in a language like C.

If any of the above is incorrect, I'm interested in learning more.

reply
dang
16 hours ago
[-]
What was incorrect (to use your word) in your posts in this thread was your misalignment with the intended spirit of the site. You responded with generic/shallow objections to someone's creative work. That's one of the failure modes of internet discussion, which is why both the HN guidelines (https://news.ycombinator.com/newsguidelines.html) and the Show HN guidelines (https://news.ycombinator.com/showhn.html) ask commenters not to do this.

It's true that the repliers crossed into the red as well, but fundamentally that's a healthy immune response going a little too far.

reply
yodon
14 hours ago
[-]
dang - thanks for being the incredible mod that you are. Genuinely hoping you one day write a book or give a talk on effective communication. Your words, even when they amount to "dude, you screwed up", are always informative and inspiring.
reply
dang
13 hours ago
[-]
Thanks for the kind reply! I'm always surprised when people react so nicely to one of those. It lands, and I appreciate it.
reply
hu3
18 hours ago
[-]
There's nothing to dispute in your assertion, because you're technically correct.

However it's just not constructive and repetitive. You're basically walking into a bar and yelling that alcohol is unhealthy.

reply
dang
16 hours ago
[-]
This response is right on fundamentals (more at https://news.ycombinator.com/item?id=45530668) but unhelpful in approach, and ends up breaking the HN guidelines in its own right.

Rather than flaming someone for not responding in the intended HN spirit, and invoking their recent post as a gotcha, it would be better to take that post (https://news.ycombinator.com/item?id=45340298) as evidence that they want the same things that you (and we!) do, and base your response on that.

Nearly everyone here wants great discussions; the problem is that we all underestimate the provocations in our own comments, or even just don't see them at all. Meanwhile the provocations in other people's comments often land much harder on us as readers. Say the skew is 10x in each direction—that leads to a 100x distortion. This "100x problem" is probably at the root of most interpersonal glitches here (and not only here). Unfortunately, it seems to be a deep and universal bias.

reply
johnisgood
13 hours ago
[-]
I know I am not supposed to engage here, but... you can write "unsafe" code in many languages. Logic bugs are extremely common, for one. So are you implying that there exists a language where someone "always writes safe code"?

Indeed, a web server needs to be "safe". How do you know this project is not safe? Have you even tried it, let alone review it, or did you just see "in C" and automatically assumed it is not a safe web framework?

I am pretty sure the author of this project is thrilled to wait for you to submit issues or even PRs.

reply
yodon
11 hours ago
[-]
Logic bugs can exist in any language. Buffer overruns, unallocated memory uses, use after free, pretty much all of the first generation of internet vulns, those are signs the code was written in a non-memory managed language.

The fact that you CAN write memory safe code in C does not mean all maintenance programmers of your project will always write memory safe code in all their commits.

Memory managed languages unquestionably reduce the surface area of bugs one has to worry about, and in particular they eliminate the class of vulnerabilities that was most prevalent in web servers prior to the widespread adoption of memory safe languages.

reply
johnisgood
3 hours ago
[-]
You're going in circles. You've made the same point three times now without adding anything new.

Yes, memory-safe languages eliminate vulnerability classes. I said that in my first reply. Yes, people make mistakes in C. Obvious. None of this tells us anything about this specific web framework.

You dismissed it as "a terrible idea" based on the language alone. That's lazy analysis. Either review the actual code and find the bugs, or admit you're just cargo-culting the "C bad" narrative without looking at the implementation.

Have you actually examined this codebase or not?

reply