# I am the Watcher. I am your guide through this vast new twtiverse.
# 
# Usage:
#     https://watcher.sour.is/api/plain/users              View list of users and latest twt date.
#     https://watcher.sour.is/api/plain/twt                View all twts.
#     https://watcher.sour.is/api/plain/mentions?uri=:uri  View all mentions for uri.
#     https://watcher.sour.is/api/plain/conv/:hash         View all twts for a conversation subject.
# 
# Options:
#     uri     Filter to show a specific users twts.
#     offset  Start index for quey.
#     limit   Count of items to return (going back in time).
# 
# twt range = 1 6513
# self = https://watcher.sour.is?uri=https://lyse.isobeef.org/twtxt.txt&offset=3034
# next = https://watcher.sour.is?uri=https://lyse.isobeef.org/twtxt.txt&offset=3134
# prev = https://watcher.sour.is?uri=https://lyse.isobeef.org/twtxt.txt&offset=2934
@prologic There's a wee bit more destruction than on your previous two photos. But it still looks quaint to me.
@abucci The only exception is C++.
Plan A: Go to Esslingen with the canoe. That was foiled by blocked off streets for a bicycle race. It was impossible to get anywhere where we could enter the river Neckar.

Plan B: Lake Max-Eyth in Stuttgart. On the way we encountered plenty of other blocked off raods from the bicycle race and one particular traffic light let only four cars pass at most. It took us 15 minutes alone on that crossing. Then all the myriads of road works, man. It once again proofed that only insane people go to Stuttgart by car. Two bloody hours later at the destination the green-blue algea put its spoke in our wheels. Definitely avoid skin contact with that water. Downwind it smelled horribly.

So then for plan C we hiked a little bit down the riverside (the lake is next to the river) to find a shady place where we could picknick. We finally found a spot among a tree and spent some nice hours.

Yeah, super preparation on our end. But who would think of a bicycle race in the first place?
Heavy rain just started! \o/
Heavy rain just started! \\o/
@movq I'm glad for the declining temperatures. Cool view. It's about time for autumn to finally come.
We need that much water over here, too, @stigatle. Ground water is super low. I read somewhere the other day that we're facing the severest drought in a hundred years or so. Yes, @prologic, the clouds were looking very nice.

Today was supposed to rain the whole day, but not a single drop up to this point. Maybe in an hour the weather front will be here. Hopefully!
I can't make it to the call later.
@prologic You must have picked the best spots, it doesn't look too bad on your photos. ;-)
Thunderstorm and rain missed us here once again. :-( It slightly cooled off this evening, but we still had about 24°C. In the forest it was okay, but when the wind picked up, it threw hot air on us. Just felt like a massive hair dryer. On top of the montain we saw lightning and rain in the distance. And there was constant growling of thunder far away.

Rain missed us
@movq ;-) @prologic Yes.
@eaplmx There's basically one mate with whom I communicate in encrypted e-mails. The subject must of course some random garbage. But all other people don't care about it. Sadly.
@prologic Uuuhhh, very nice! I'd love to be at this place right now. :-)
@movq What!? Maybe you should send a bug report…
@movq Hahaha, this is great! :-D

@prologic The wording is a bit weird: "The James Webb Space Telescope does not send its data as complete pictures but rather column of numbers representing brightnesss values." Now we're wondering what the authors thought how images are transmitted in general. (Or what the uncited source actually meant.)
@prologic Ta! :-)
Whooaaah, hell yeah! Check out Cody's magnificant limestone soap. It looks (and works) fantastic. Now I very badly want to make this myself, too. :-)
Solutions: 02, 03 and 04
@stigatle Very lovely!
When sweeping leaves three quarters of an hour in the hot afternoon sun I successfully got myself a blister on each hand. Not the best decision today. Went to bed for a siesta and then quickly walked to the dairy farm. I missed the first, very best part of the sunset and only got the second and third phases.

Sunset
@abucci No doubt, everybody can do what they want, it's their channel after all. I'm fully with you there. All I say is that "subscribe, bell, like, comment" every time is rediculously silly in my opinion.
@movq Perfect! Now only 02 and 03 are left. ;-)
@movq Haha, Moonlight reminds me of Rammstein. ;-)
@prologic Tell your kids to inspect two, three and four. :-)
@prologic @abucci This also makes me mad. I doubt that there is a single person out there who did just that because of those monkeys telling them each and every time. Preferrably multiple times a video. They know their audience. It's one of the dumbest out there. I once saw a video where YouTubers got asked whether they actually do smash the bell and wack the like button themselves when being told. No surprise, none of them ever did.

Luckily, there are still a few channels out there who are not aggressively promoting this shit. They are rare, but they exist. I either skip forward, stop the video or just don't watch these videos anymore if it gets too overboard.
@prologic @mckinley A bot/thread/whatever announcing it in advance would be good.
My mate and I went on a hike. 25°C are quite hot. Being a Sunday and with all the sunshine there were tons of people out there.

Apples on a tree

Not sure what's the deal with the red "sold" label on that tiny tree in the forest.

Who spots the frog?
@prologic Exactly.
@prologic That's why you only store what's really, really, really required. Not a single bit more. In that case: For the poll a title and its options. And for a vote the name and selected options. That's it. Strictly speaking there's no need to store the submit timestamps. Okay, some form of IDs are also necessary I reckon, but that's it in its purest form. I wouldn't even go with comments. If you actually want to build a distributed system, a can of worms is going to open. How're you gonna handle collection of votes from somewhere else and all that jazz.

For my needs a central system with the obove stated requirements would completely do it.
@mckinley Whoa, crazy! I have to try this. :-)
@xuu Just to be extra sure! :-D
@abucci It's rare, but you might want to reset the stream (if it supports it) or do whatever else later on, so automatically closing would defeat that. On the other hand I never close stdin/out/err. I was under the impression that this is not needed. Rereading I see that it's pipe.
@darch I agree, use a well-established library. Usually the standard library already gets you covered for this day and age. Doing it yourself (except for educational purposes) will fail miserably.
@darch Hahaha, right. :-D
@abucci Being a software guy myself I can easily relate to that. And to be fair, most of the time it's software to rightly blame.
@abucci This oily fractal is looking very nice!
@darch Ah right! If I'm not mistaken that was part of @prologic's Doom experiment thingy. And then we drifted to your OpenGL animations. :-)
@abucci Always a waste of time when debugging software leads to a hardware issue eventually. Glad you finally solved it. :-)
@prologic Sounds like a very interesting idea. It probably would have a mix of a pull and push model. Pull to get updates and push to invite others to take part of a poll. Although, I would be already happy if there is a super simple web tool tool out there without all the fuzz and not collecting all sorts of random data.
@prologic You mean a decentralized event date poll system (or what ever the correct term is)?
@movq I'm not into black metal, but I would have thought that these covers are even darker than what I've taken here. Anyway, if there are any bands out there, feel free to use them. ;-)

Bwahahaahaaahaaaaa, this is great! :-D
Good morning, @stigatle, what are you going to program?
@tel That's one drawback, indeed. To view images in Nesboat I open them in my browser. Luckily, I don't have many image feeds. I also tried a macro to launch feh, but since macros still need an additional keystroke, I quickly fell back to just o or 1 or whatever the respective link number is. I read somewhere™ that some terminals might be able to render images, but I never tried it. Need to look into that some day.
@ocdtrekkie Ah, nice. Only caveat is PHP. ;-) I've also written an appointment finder, but that only let's you vote on a date. No possibility to also select time slots. Probably need to expand that.
Ha, found @tel! Mystery solved.
@prologic @ocdtrekkie @mckinley @darch @Tel It was very nice, indeed! No idea who Tel was, though. Please identify yourself! ;-) I also missed the first 15 minutes since I was in the wrong call and didn't take any notes either, but here's what I remember from the top of my head:

* Ocdtrekkie's home and car automation
* Prologic wanted to try some Go Doom clone or something like that
* Fixing darch's Bad Request in the conversation view by removing superfluous csrf_token cookies other than for /, refactoring plans of CSRF handling in yarnd
* How people getting scammed in all sorts of different ways
* Pronouncing Twtxt
* Following YouTube channels
* Subscribing to RSS and Atom feeds with Newsboat, Tiny Tiny RSS and yarnd
* And probably a lot more
:-D I had to reboot to fix the docking station. Even my mouse didn't want to work when plugged into the laptop-internal USB ports before. Phew, what a relief.
@darch In 15 minutes.
@prologic Hahahaha! :-D Sounds about right. ;-)
And now we're a bit past 180°… Strange. When did that happen? And how? Right, @movq, it might not even have survived the bumpy, rough path. ;-) @abucci Kaboom!
Look at that. Rain for basically the whole day. Awesome! When we made our appointment the other day we didn't know that we picked the perfect time for today's hike. It just stopped before we went outside.

All these hot temperatures for weeks made the 20°C today feel quite cold. After lunch I had to wear a jumper, cool air coming in was too much for only a t-shirt when sitting still at the desk. But walking this evening was very lovely. Absolutely perfect in a t-shirt. We thought we will encounter some more rain on our way, so we opted for long trousers. Yet, it remained dry, so we both regretted our choice quickly. The humidity of at least 100% was very tough. However, no comparison to temperatures of last week and the one before.

Fog everywhere

Noone else was outside, we had everything for us. Met exactly six people in total in those two and quarter hours. In general this was a very silent trip. No birds singing, no traffic on the one road for several minutes. Heard just a bunch of sheep and cows in the distance. Quite weird, but enjoyable for sure.
@darch @prologic @mckinley I avoid this Doodle crap¹, but I'll be there at 5:00 UTC.

¹ Last time I checked: "We value your privacy, accept all cookies" No you do not, fuck off.
What on earth!? Just noticed, the clock face of my watch is shifted 90° clockwise. Was today's bike ride on the gravel too much of a raddle? Hm. Crap.
@prologic I know. :-)
User account registration at twtxt.net was reenabled just a bit too early again. :-)
@xuu Congratulations! That's by far the longest twt I've seen. :-) Working fine in tt. I was a bit worried, that lengths of multiple screens would be hard to read or scroll. But magically it just scrolled line by line.

I reckon two returns can be saved in GetUser(…). Oh, on further inspection there are even two nested err != nil checks.

Generics were desperately needed. I'm glad they finally introduced them. I stumbled across them last week and gave them a shot. The syntax is probably not the very best, but I will get used to it eventually.
@eaplmx Absolutely, I fully agree.
@xuu Thanks, mate! Arms
@movq Uuuuuuuhhhh, nice! :-)
@movq Kind of, I know that August is great for the perseides. So I thought I'll give it a quick shot. Given, that I just do that for at most a minute each night, I didn't expect to actually be that lucky and see anything. Looking at the nice moon and the Ursa Major is generally all I can ask for.

Comet is also not too shabby. ;-)
@mckinley Ta! KMail goes two steps further. The blue pane appears already while typing up the e-mail. When hitting Send, the Attach File… button in the message dialog is the default, minimizing chances of sending the mail without any attachments when just pressing Enter out of a habit. Yup, also experienced that a couple of times. :-)

And now I nearly missed adding the screenshot to this twt! Good thing I proofread it three times.

KMail detecting a potentially missing attachment
@movq Hell yeah, these are some real treasures! \\o/ This is my favorite one. I like how the exhaust blends with the moon. And this is a great shot, too. Well done, mate! Some time well spent.

Just before going to bed yesterday I went outside to take a quick last look at Ursa Major. I wasn't even even standing five seconds there and I saw a meteor for barly half a second. Truly amazing!
@movq Hell yeah, these are some real treasures! \o/ This is my favorite one. I like how the exhaust blends with the moon. And this is a great shot, too. Well done, mate! Some time well spent.

Just before going to bed yesterday I went outside to take a quick last look at Ursa Major. I wasn't even even standing five seconds there and I saw a meteor for barly half a second. Truly amazing!
@mckinley I have a high prejudice against GTK, but thanks for the tip! At least they also go the OK/Apply/Cancel route and don't just offer a Close button in the settings dialog. That sounds promising. Their feature list is also very nice. I'll take a closer look next time KMail maroons me. One question, does it warn about missing attachments? That feature I first saw in KMail saved me thousands of times.
@abucci KMail is actually fairly good, it does basically all I need. Problems started when they introduced this Akonadi shit. This just calls for trouble every now and then. On my old machine the database even got corrupted irrepairably. Starting over with a maiden one I ended up with another broken state beyond repair a few weeks later. Still today Akonadi, must be kill-9-ed from time to time.

KDE 3.5 had the very best KMail versions ever. Very stable, no bugs I encountered (although the bug tracker was full of bug reports, too). With 4 and 5 lots of little issues got introduced that haven't been there before and plenty are still unresolved today. I sent a couple of bug reports for KDE software, but never got any reactions. Once, six or seven years later a dude came back and asked whether a particular bug had been fixed in the meantime. By then I had long migrated off that program.

With KDE 5 I have to fake XDG_CURRENT_DESKTOP=KDE or else with an i3 value, icons are not found in KDE programs anymore. Yeah. Also KMail's message text pane does not have any borders around the header part when I start KMail (left). Opening the settings and closing the dialog with OK without changing anything fixes it. It then decorates the headers properly (right). No idea what's going on there.

KMail does not decorate the header properly, when I start it, hitting OK in the settings dialog without changing anything fixes it

Also gave Thunderbird a shot a couple of times, but it never worked out for me. One major issue was the broken rendering of quotes when composing I think. And also a bunch of other things I don't remember anymore.

@movq Absolutely, HTML mails are an invention straight from hell! I even only send plain text mails at work as the only guy.
Looking at the Qt WebEngine docs on debugging helped: QTWEBENGINE_CHROMIUM_FLAGS="--single-process" kmail surpringly works… O_o WTF. This goes in the /usr/local/bin/kmail wrapper script then.
Bloody hell, my system update affected the libc, libc6, locales and golang-1.19 packages and after a reboot KMail is fucked. Uses 100% CPU and doesn't show any mail texts anymore. Starting it in a terminal then shows heaps of org.kde.pim.webengineviewer: WebEngine render process crashed being printed to stdout.

https://bugreports.qt.io/browse/QTBUG-73293 Closed as invalid.

https://bugs.archlinux.org/task/61534 suggests to QTWEBENGINE_CHROMIUM_FLAGS="--enable-logging --v=3" kmail, unfortunately, nothing useful shows up:


[32957:1:0813/153712.828577:VERBOSE1:sandbox_linux.cc(69)] Activated seccomp-bpf sandbox for process type: renderer.
[32491:32555:0813/153712.854714:VERBOSE1:gles2_cmd_decoder.cc(3850)] GL_EXT_packed_depth_stencil supported.
org.kde.pim.webengineviewer: WebEngine render process crashed


Why is there not a single decent e-mail program out there? Anybody using only mutt?
@prologic I reckon pretty much the same like you did. I haven't looked at this OpenAI API, in case it is simple enough, I would probably not have used their client library, though. Again, no idea if that would be feasible. I like WTFPL and even got a proper answer when trying to ask about it on your demo. ;-)
@prologic No doubt about that. Typing them up took me pretty much exactly an hour each. ;-) Thanks, bookmarked!
@movq Absolutely. It's more blurry than you probably hoped for, though.
@movq Mate, this looks fantastic! :-D
@mckinley Cool. Do you plan on writing an article on your time shell script? I was quite happy to see basically simple (in practice could be more complicated) things like this cobbled together in a short time. The script itself would also suffice, but maythere there is more to tell. :-)
@xuu Yup, metrics are a different story. Need to take a deeper look at them some day. With logs I meant analyzing why requests could not be processed. It's not necessarily the actual service that has a problem.
@off_grid_living Wow, that's a lot! On the other hand, your garden is also large. Overall progress is looking great. Soon, your only worry is rodents.
@prologic Exactly, errors are part of the interfaces. The only problem is that I cannot formally express it in detail so that the Go compiler would give me any hints or step on my foot. Java's checked exceptions are a mess, too. So, no idea how to solve that in an ideal world.
Alright, something for you to laugh at. ;-) I just discovered the `:target` CSS selector. I always thought that highlighting a specific comment or what not was done with JS.
@prologic Yeah, that was very nice. :-) In reality more violet than the photo shows.
@prologic Right, it's not inheritance, but embedding. The two standard errors are cool. But always doing basically the same for all our own errors with probably also implementing Unwrap(), Is(…) and As(…) is sooooooo much work. Unnecessary work, there must be a better way. Sleeping on this twice, the main issue is probably not carefully thinking about the errors in my APIs. Which kind of errors should be distinguishable by the caller. Does it even make sense to differentiate between them? Can the caller react differently depending on what went wrong? This also depends on the caller, of course. In my combinedlog.parseLine(…) example it's basically stupid. One generic error is enough.

Logging only a single line is often very useful. But apart from access logs in web servers I can't remember seen this implemented anywhere in the wild.
@mckinley Haha, while composing I was wondering two or three times whether I should throw my thoughts in an HTML page instead. But out of utter laziness I discarded that idea. ¯\_(ツ)_/¯_
@mckinley Haha, while composing I was wondering two or three times whether I should throw my thoughts in an HTML page instead. But out of utter laziness I discarded that idea. ¯\\_(ツ)_/¯_
@abucci Writing Python and Makefiles, I actually never have any trouble there. All my editors are configured properly.

The fundamental error was to enable people making tabs arbitrarily wide. :-)
@abucci Yup. Just send them a merge request with a fix. :-)
Now, looking at @xuu's es, I feel quite a bit stupid. In my particular case I could have just put the %w at the front of the message and not at the end and get exactly what I want: fmt.Errorf("%w '%s'", ErrInvalidSentBytes, sentBytes) results in "invalid sent bytes '4385743057573509732574375098741128354092547569201274123'" and can be error.Is(err, ErrInvalidSentBytes-asserted. No idea why I did not think of that. O_o Thanks mate! :-)
@prologic In one project a bunch of work mates strongly advocated a new to everyone (them included) idea. Any incoming request must produce exactly one log line. Not more and not less. Exactly one. That way the log does not get spammed with lots of useless information most of the time and one immediately sees what went wrong, if at all. In the beginning I thought this is completely rediculous, because I had never seen this anywhere and thus just couldn't imagine that this will work at all.

The technical details to only produce one log per request were sorted out fairly quickly with a customer logger, that just replaces the last message with the newly logged one and finally at response end actually logs it. When a Java component was completely rewritten in Go they tried it out and I was very surprised that it worked that well for the analysis. I basically never missed any other surrounding logs that would have been produced in the old log flooding style. Over time a few things such as structured context fields were added that turned out to be useful to have for error analysis. It's been a couple of years, but I think we rewrote that logger a bunch of times to optimize even further and try out new API ideas we had.

I remember it as a surprisingly successful experiment. In my current project I also once tried to tell my work mates about that, but – just like me when I heard about it in the first month – they weren't ready for it. :-) To be fair, we have a slightly different situation now than in the other project.
@prologic Error handling especially in Go is very tricky I think. Even though the idea is simple, it's fairly hard to actually implement and use in a meaningful way in my opinion. All this error wrapping or the lack of it and checking whether some specific error occurred is a mess. errors.As(…) just doesn't feel natural. errors.Is(…) only just. I mainly avoided it. Yesterday evening I actually researched a bit about that and found this article on errors with Go 1.13. It shed a little bit of light, but I still have a long way to go, I reckon.

We tried several things but haven't found the holy grail. Currently, we have a mix of different styles, but nothing feels really right. And having plenty of different approaches also doesn't help, that's right. I agree, error messages often end up getting wrapped way too much with useless information. We haven't found a solution yet. We just noticed that it kind of depends on the exact circumstances, sometimes the caller should add more information, sometimes it's better if the callee already includes what it was supposed to do.

To experiment and get a feel for yesterday's research results I tried myself on the combined log parser and how to signal three different errors. I'm not happy with it. Any feedback is highly appreciated. The idea is to let the caller check (not implemented yet) whether a specific error occurred. That means I have to define some dedicated errors upfront (ErrInvalidFormat, ErrInvalidStatusCode, ErrInvalidSentBytes) that can be used in the err == ErrInvalidFormat or probably more correct errors.Is(err, ErrInvalidFormat) check at the caller.

All three errors define separate error categories and are created using errors.New(…). But for the invalid status code and invalid sent bytes cases I want to include more detail, the actual invalid number that is. Since these errors are already predefined, I cannot add this dynamic information to them. So I would need to wrap them à la fmt.Errorf("invalid sent bytes '%s': %w", sentBytes, ErrInvalidSentBytes"). Yet, the ErrInvalidSentBytes is wrapped and can be asserted later on using errors.Is(err, ErrInvalidSentBytes), but the big problem is that the message is repeated. I don't want that!

Having a Python and Java background, exception hierarchies are a well understood concept I'm trying to use here. While typing this long message it occurs to me that this is probably the issue here. Anyways, I thought, I just create a ParseError type, that can hold a custom message and some causing error (one of the three ErrInvalid* above). The custom message is then returned at `Error()` and the wrapped cause will be matched in `Is(…)`. I then just return a ParseError{fmt.Sprintf("invalid sent bytes '%s'", sentBytes), ErrInvalidSentBytes}, but that looks super weird.

I probably need to scrap the "parent error" ParseError and make all three "suberrors" three dedicated error types implementing Error() string methods where I create a useful error messages. Then the caller probably could just errors.Is(err, InvalidSentBytesError{}). But creating an instance of the InvalidSentBytesError type only to check for such an error category just does feel wrong to me. However, it might be the way to do this. I don't know. To be tried. Opinions, anyone? Implementing a whole new type is some effort, that I want to avoid.

Alternatively just one ParseError containing an error kind enumeration for InvalidFormat and friends could be used. Also seen that pattern before. But that would then require the much more verbose var parseError ParseError; if errors.As(err, &parseError) && parseError.Kind == InvalidSentBytes { … } or something like that. Far from elegant in my eyes.
@prologic Glad you still enjoyed it. ;-)
@prologic Let me think. Three things come to mind. I wasn't aware of all the history of ASCII and the exact reasons why some symbols are placed where they are. Pretty genious. Also the French Russian pen friend parcel is a cool story I never ran across.

Although there are a bunch of Ukrainian license plates around here, I never wondered why I actually could read all of them without issues. Nice trick to just limit the Cyrillic script to the ones that look like Latin letters.

Also the bad switch dropping a byte every now and then and this way producing Chinese characters was really fun. Good to know, it would have never occurred to me. Not in a hundred years.

I'm sure there are many more things I learned and already forgot again. :-)
Crazy how fast it is getting dark now! Suddenly, pitch dark. The water line of the tad pole pond lost roughly half a meter. Basically all creeks are bone dry for weeks now. We just came across a single small trickle that still had a few drops of water.

Sunset

We saw a super nice, large, orange moon raising over the horizon. But my camera couldn't pick it up.
@abucci It's been a long time ago, that I looked at the yarnd code. Maybe you need to follow those people to mention them automatically? No idea. I just remember that it was changed. In the beginning there were quite a lot of mentions filled in when replying and then people decided against that behavior for some reason I cannot recall anymore.
@abucci That cheat sheet is in fact a great idea! I should compile one for all my commonly used languages, too.
@movq @abucci Yes, formatting with a special date is incredibly silly. I couldn't believe it myself either the first time I ran across it. It still drives me nuts every single fucking time I have to deal with it. Not sure why the Go folks still don't consider it a failed experiment. Luckily, I can often just use the time.RFC3339 or time.RFC3339Nano constants and don't worry about it.

Maybe the positive effect is that you're forced to always go to the docs to look everything up when writing the special time pattern, because there's no chance of remembering anything at all (maybe except the year 2006). With the letter system you might think you know what you do and then skip that check in the docs and finally fail because it was the other way around again. If the Go maintainers wanted to prevent that, then they actually succeeded.

It really depends on the ecosystem you're in. The lower date and upper time rule e.g. doesn't work for Java: yyyy-MM-dd'T'HH:mm:ssSSSXXX (not sure on the exact number of Xs though).
I forgot one thing. Testing for errors is also an important part that is often overlooked in my experience. Another rule we talked about two hours ago is to test error messages preferably exactly. Then there's a dim chance of spotting a final garbage error message. When seen in total, one might tell if it is understandable and all important context information are present. Lots of error messages I've come across are completely useless, I've no idea what's going on or what I have done incorrectly. A frightening lot of times messages don't even make any sense at all. Not a single bit. Just random words put together. The really bad ones you don't understand even if you look at the code and exactly know what the situation is but still cannot decipher the message with all that knowledge on top. It happens more often that I would think.

No doubt, writing good error messages is an art in itself and often takes a minute or two (or even more) to come up with something short and still precise. But in the end it will always pay off to provide some quality message. Same with logging in general, of course. But errors returned to somebody else are more important than internal logs.

In a previous commercial software project the customer wanted to have a complete catalog of all info log messages and above. An additional description with more context had to be provided what that log ID and message meant. I think with warning level and above both a solution and verification was required on how to fix it and then validate that it actually worked. Error and fatal included even more stuff I can't remember anymore.

For us developers that was incredibly annoying, but when we then finally also had to operate that software, this was absolutely awesome to have! Man, did I suddenly understand what all this effort was for. It immediately paid off. There was one guy inhouse just analyzing logs from our different systems all day long and trying to categorize and correlate things. Even with the log message catalog he often had some detail questions to use developers. Can't imagine what would have happend without that catalog.

That experience was truely an eye-opener for me. I can also see it with my current work mates. Only if you had been forced to analyze yourself with nothing else but the logs what was going on or went wrong, you will appreciate and also write good messages yourself. If you haven't been in that situation before, there's basically no way you'll be in a position to write decent logs. And even then you realize that important context is missing when you have to analyze something. :-)

I'm on the fence with testing log entries. In a previous project we quite often did. But there were also hard requirements to produce certain logs, so then it made sense. Usually I don't unless there are some weird circumstances. Can't think of any such situation off the top of my head right now, though.
@prologic Yup, looks good, I also agree with her.

Just a few weeks back I had basically the same idea with inventing a more generic mock implementation for our storage layer at work. Previously, we had tons of new test storage types each implementing another hardcoded behavior based on the exact input. For a start that works well and is incredibly easy, but over time it quickly becomes unmaintainable and also reading the tests is extremely hard. Why is that weird value used as an argument over here? Quite some time later one realizes: Oh, right, it will then trigger that and that.

So my approach basically boils down to the exact same thing Jessica does. To be able to set a mock function in the mocked object that will then do whatever is needed. Setting up more involved tests is now concise and readable. It's still not perfect, but a large improvement even so. My implementation goes a bit further than Jessica's and falls back to the real functionality, if not overridden explicitly. This has the advantage to just throw together a bunch of tests without mocking *everything*, since there are often a lot of steps needed to build the actual scenario.

In Kraftwerk v2 I extended the mock storage to be able to be initialized even more easily with this automatic init(). At work where this mock.Storage type *inherits* (and not just contains a) memory.Storage forces us to also explicitly create a memory storage for each and every mock.Storage{Storage: memory.NewStorage(…), …}. One day, if I have some time, I'll refactor the day-job code and apply this simplification, too. Ideally, Go would allow me to write some constructor thingy where I could set up and propagate initial data to the backing memory implementation. Then there's no chance of forgetting a call to the s.init() in a new function. But that's the best I've come up with so far. I just want to make it as easy as possible to write tests.

So that was very cool for me to see her writing it down as well. It seems my idea the other day was not completely silly. :-) Haven't seen it anywhere else up until now.

This test subject fits perfectly. Just before quitting time two work mates and I discussed about tests. And one rule we made up now is to prefer table tests, when possible. This helps writing and maintaining better tests. I remember back in the Java days when there were different parameterized test frameworks, as they called it. They worked similarly, but in contrast to Go's flexibility of the builtin table tests, it doesn't really compare. Arguably, it's still heaps of code in Go, but creating parameterized tests in Java was always much more hassle in my opinion. Oh, I need this special runner now, which is the correct one? What was the annotation called again? Oh hang on, now these other tests won't work anymore with this new test runner, I have to move stuff to new test classes. That's why I only rarely used them. With Go, it's a real first-class citizen and not an afterthought and that positively shows. (Not sure if parameterized tests improved after Java 8.)

One thing that the article doesn't mention, or I already forgot after writing this wall of text. ;-) Thinking about edge cases. That's super important and often they're missed in my experience. Here TDD might be a good approach to the problem. Come up with possible cornor cases up front, write some tests and then implement the logic. At least for bug fixes this is a great way. There are limitations of course, if you don't know in advance how your going to design the API, TDD won't work in practice. We just had exactly this sitation this week at work. Even with only one fairly simple new function in the end. We threw away four (!) designs and did it quite differently for the final result. If we had strictly followed TDD here, we would have rewritten all our tests a couple of times. And that would have been super annoying and thus demotivating (well, we had to completely rework them once). Granted, that doesn't happen thiiiis often, but it still occurs every now and then.

One last final thing: I very much enjoy looking at code coverage reports and see a lot of green there. This motivates me writing more tests and thinking of ways I could test that last little thing here as well. And if that turns out to be impossible with reasonable effort, you know that you probably need to refactor things.
Super fun, @movq, yes! You won't regret it. :-)
Pike Matchbox! https://www.youtube.com/watch?v=_mZBa3sqTrI Very interesting talk about plain text and encodings. I knew a bunch but alo learnt quite a lot of stuff. Highly recommended.
@prologic It's been a while, but your code I've seen so far didn't look too bad to me. I remember we had discussions about missing tests, but other than that, I can't recall any "oh dear, WTF" momements. Obviously, there's always something, that can be improved, nobody writes perfect code. Only close to perfect. :-) Being sick and having time pressure doesn't help writing good code either. So, don't take the harsh feedback *too* seriously. Let a week pass and have a look again, your perspective might have shifted and you possibly understand what they wanted to tell you, assuming they wanted to give you honest feedback. Buck up! :-)
Hahaha, great talk about the Worst Programming Language Ever! :-D
That's exactly what I expected from both of you, @abucci and @mckinley. :-) Ah, right, didn't think of cbl and friends. When I quickly counted by HTTP status code, I thought it would be nice to interactively drill down further. But I couldn't answer my question of what I actually want to see or look out for, so I stopped.
@prologic You simply drown and then fall asleep forever. :-D No, drinking a large sip heals hiccups.
@abucci @mckinley @ocdtrekkie I just had a look and saw empty request lines or just HELP, because my quickly thrown together parser didn't expect this sort of crap. Any particular tools you use to analyze your logs with?