Skip to content

Commit

Permalink
feat(blog): New article on Good Errors
Browse files Browse the repository at this point in the history
* feat: new design

* fix: progress

* feat: add iron widow to catalogue

* chore: update to latest

* feat: lots of progress

* fix: move away from imdb-api since it's getting paid

* fix: new entry

* fix: optimizei mages

* feat: mobile support

* feat: some sort of dark mode

* fix: build

* feat: new entries

* feat: new design

* fix(wiki): Update File Picker Tragedy to reflect current state

* feat(catalogue): Add new entries

* fix(misc): Small fixes

* feat(catalogue): Add entry

* feat(blog): New article on Good Errors
  • Loading branch information
Princesseuh committed Jul 2, 2023
1 parent 0377760 commit 2b08f05
Show file tree
Hide file tree
Showing 39 changed files with 429 additions and 21 deletions.
2 changes: 1 addition & 1 deletion scripts/getInfoCoverBook.ts
Expand Up @@ -26,7 +26,7 @@ export async function getDataForBooks() {
const booksDirs = getContentDirs("books");

for (const bookDir of booksDirs) {
const dirBasename = path.basename(bookDir.pathname);
const dirBasename = path.basename(decodeURI(bookDir.pathname));
Logger.info(`Getting data for ${bold(dirBasename)}...`);
const dataFilePath = new URL("./_data.json", bookDir);
if (fs.existsSync(dataFilePath)) {
Expand Down
2 changes: 1 addition & 1 deletion scripts/getInfoCoverGame.ts
Expand Up @@ -45,7 +45,7 @@ export async function getDataForGames() {
const gamesDirs = getContentDirs("games");

for (const gameDir of gamesDirs) {
const dirBasename = path.basename(gameDir.pathname);
const dirBasename = path.basename(decodeURI(gameDir.pathname));
const dataFilePath = new URL("./_data.json", gameDir);

Logger.info(`Getting data for ${bold(dirBasename)}...`);
Expand Down
4 changes: 2 additions & 2 deletions scripts/getInfoCoverMovieShow.ts
Expand Up @@ -23,7 +23,7 @@ export async function getDataForMoviesAndShows(type: "movies" | "shows") {
const moviesShowsDirs = getContentDirs(type);

for (const movieShowDir of moviesShowsDirs) {
const dirBasename = path.basename(movieShowDir.pathname);
const dirBasename = path.basename(decodeURI(movieShowDir.pathname));
const dataFilePath = new URL("./_data.json", movieShowDir);

Logger.info(`Getting data for ${type}/${bold(dirBasename)}...`);
Expand Down Expand Up @@ -60,7 +60,7 @@ export async function getDataForMoviesAndShows(type: "movies" | "shows") {
const coverPath = new URL("./cover.png", movieShowDir);

if (!posterURL.endsWith("png")) {
sharp(coverData).toFile(coverPath.pathname);
sharp(coverData).toFile(decodeURI(coverPath.pathname));
} else {
fs.writeFileSync(coverPath, Buffer.from(coverData));
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/articleList/ArticleListAside.astro
Expand Up @@ -26,7 +26,7 @@ const years: Array<[string, number]> = [
{
tags.map((tag) => (
<li>
<a href={"/articles/tags/" + tag[0] + "/"}>{tag[0]}</a> ({tag[1]})
<a href={"/articles/tags/" + tag[0] + "/"}>{tag[0]}</a>&nbsp;({tag[1]})
</li>
))
}
Expand All @@ -38,7 +38,7 @@ const years: Array<[string, number]> = [
{
years.map((year) => (
<li>
<a href={"/articles/years/" + year[0] + "/"}>{year[0]}</a> ({year[1]})
<a href={"/articles/years/" + year[0] + "/"}>{year[0]}</a>&nbsp;({year[1]})
</li>
))
}
Expand Down
1 change: 1 addition & 0 deletions src/components/catalogue/Core.astro
Expand Up @@ -52,6 +52,7 @@ const pageLenth = 30;
display: block;
font-size: 0.875rem;
line-height: 1.5rem;
font-weight: 600;
}
</style>

Expand Down
Binary file added src/content/blog/gooderrors/error-overlay.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions src/content/blog/gooderrors/gooderrors.mdx
@@ -0,0 +1,92 @@
---
title: What's in a good error
tagline: "Because good DX goes from W to N: Working to Not Working"
date: 2023-07-02
tags: ["dev-experience", "programming"]
slug: "gooderrors"
---

Recently, well, last November I was tasked with improving the error messages and overall experience around errors in [Astro](https://astro.build) (so, we're talking errors for users who are developers, here).

To be honest, I didn't necessarily have any experience in the domain. I just code monkey all day and sometimes the errors messages are good and sometimes they're bad, for some reason.

Since I usually do try to be proud of the work I do, I studied various projects, watched many talks and learned a bunch. Here a few of my takeaways.

## Just don't have errors (lol)

Ultimately, errors always suck. Even the nicest and most informative messages will always be a bad experience for the user, things are funnier when they work.

Unless your software is very important getting to an error state shouldn't be impossible, but it should be hard. The usual things all applies here: Intuitive APIs, good documentation and other resources, [helpful tooling](/article/dontunderestimateeditorintegration)... All of those are as important, if not more than good errors.

## Good errors are honest

A mistake I see too often is not taking into account the user's point of view. This come in multiple forms:

- Over technical messages that are ultimately unhelpful (`0x75ba98: index out of bounds`)
- Trying way too hard to be kind and goofy (`Oopsy daisy! There was an error with accessing this index!`)

I'd like to bring particular attention to the second one, because while it can be seen as the most "human" or "respectful" one, it's a common mistake to forget the most important part of error messages: You only see them in bad times.

> This tone minimizes the significance of the error, which is important to the developer. The developer may be frustrated and your error message shouldn't be making jokes about their struggles.
>
> -- <cite>The Astro Error Writing Guide</cite>
In this specific example, I think the technical message isn't actually that bad. It's just lacking in useful information. Adding what index was accessed, the array name and length and removing or putting less emphasis on the memory address (Unless it's actually relevant) would probably be enough.

## Good errors are nice to look at

Both in the terminal and in the app, error messages can and should look good. Not only can they be aesthetically pleasing, the visuals can also be done in a way that maximizes the user's attention toward the key elements that would help them solve their issue.

<Image
src={import("./error-overlay.png")}
alt="A screenshot of the Astro error overlay, showing the overall design and features such as clickable links and code highlighting."
/>

Having properly designed visuals also brings all the usual benefits that good design do: everything is responsive, proper accessibility was ensured and the overlay experience is lovely.

In the message themselves you can also do some visual work: add some bold to key elements, some inline code styling, maybe even italics if you have a nice hint that needs nuance.

For Astro, I added partial Markdown support that works in both the web overlay and the terminal so developers can write cool error messages in a language they're comfortable with. This also gave us the benefits of the 90s technology of: clickable URLs.

<Image
src={import("./markdown-terminal.png")}
alt="A screenshot of the Astro error overlay, showing the overall design and features such as clickable links and code highlighting."
caption="This also gave us the benefits of the 90s technology of: URLs that look clickable in the terminal"
/>

Additionally, although this is more of a writing thing than a look one, keeping the error message easy to read, concise and to the point is also important. If you have a lot of information to convey, consider adding a separate hint or a link to a resource that can help. **Users don't want to read your error messages**.

## Good errors are a cultural thing

To ensure errors are good in Astro, always and forever, it needed to be more than just "Erika did a pass once and made the errors better". Every other Astro maintainers, and future ones as well needed to know how to make good errors.

As such, the first thing I did, before even rewriting any errors was to write a guide for other maintainers. The guide [reside in a `README.md`, next to where all the errors are defined.](https://github.com/withastro/astro/blob/main/packages/astro/src/core/errors/README.md) so it's easy to find and update when needed.

## Good errors are...

Finally, a few more things that I think are important to keep in mind when writing errors:

### **Good errors don't change (too much)**.

Users should have a static way to find their errors when looking through your documentation / searching the web. Think HTTP status codes.

For Astro, we decided to include a name for each error that we don't change. Updating the message itself is fine, but the name should stay the same.

### **Good errors are actionable (if possible)**.

If you can, provide a solution to the problem. If you can't, provide a link to a resource that can help. Get the user back on track as fast as possible.

### **Good errors have contextual information (when applicable)**.

In addition to the file and line number (and maybe a box showing around the code where the error is), adding contextual information can be very helpful.

For example, `Could not render ComponentName` is more helpful than `A component could not be rendered`.

## Additional resources

Some resources I found helpful while working on this project

- [When life gives you lemons, write better error messages](https://wix-ux.com/when-life-gives-you-lemons-write-better-error-messages-46c5223e1a2f)
- [RustConf 2020 - Bending the Curve: A Personal Tutor at Your Fingertips by Esteban Kuber](https://www.youtube.com/watch?v=Z6X7Ada0ugE) (part on error messages starts around 19:17)

A lot more resources can be found by searching for "error messages" on your search engine of choice, however, I found that most of them were too general and not specific enough to the domain of developer experience, so while interesting, they weren't always helpful.
Binary file added src/content/blog/gooderrors/markdown-terminal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/content/books/pineapplestreet/_data.json
@@ -0,0 +1,7 @@
{
"title": "Pineapple Street",
"subtitle": "A Novel",
"authors": ["Jenny Jackson"],
"publishers": ["Penguin Publishing Group"],
"publishDate": 1672531200
}
Binary file added src/content/books/pineapplestreet/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/content/books/pineapplestreet/pineapplestreet.md
@@ -0,0 +1,9 @@
---
title: Pineapple Street
finishedDate: 2023-07-01
rating: "disliked"
platform: "audiobook"
isbn: "9780593654705"
---

Not for me
47 changes: 47 additions & 0 deletions src/content/games/csgo/_data.json
@@ -0,0 +1,47 @@
{
"first_release_date": 1345507200,
"genres": [
{
"id": 5,
"name": "Shooter"
},
{
"id": 24,
"name": "Tactical"
}
],
"platforms": [
{
"id": 3,
"abbreviation": "Linux"
},
{
"id": 6,
"abbreviation": "PC"
},
{
"id": 9,
"abbreviation": "PS3"
},
{
"id": 12,
"abbreviation": "X360"
},
{
"id": 14,
"abbreviation": "Mac"
}
],
"companies": [
{
"id": 56,
"name": "Valve",
"role": "developer"
},
{
"id": 150,
"name": "Hidden Path Entertainment",
"role": "developer"
}
]
}
Binary file added src/content/games/csgo/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/content/games/csgo/csgo.md
@@ -0,0 +1,9 @@
---
title: "Counter-Strike: Global Offensive"
rating: "loved"
platform: "pc"
finishedDate: "N/A"
igdb: 1372
---

Counter-Strike.
39 changes: 39 additions & 0 deletions src/content/games/darksouls/_data.json
@@ -0,0 +1,39 @@
{
"first_release_date": 1316649600,
"genres": [
{
"id": 12,
"name": "Role-playing (RPG)"
},
{
"id": 31,
"name": "Adventure"
}
],
"platforms": [
{
"id": 6,
"abbreviation": "PC"
},
{
"id": 9,
"abbreviation": "PS3"
},
{
"id": 12,
"abbreviation": "X360"
}
],
"companies": [
{
"id": 1012,
"name": "FromSoftware",
"role": "developer"
},
{
"id": 248,
"name": "Bandai Namco Entertainment",
"role": "publisher"
}
]
}
Binary file added src/content/games/darksouls/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/content/games/darksouls/darksouls.md
@@ -0,0 +1,11 @@
---
title: "Dark Souls"
rating: "masterpiece"
finishedDate: "N/A"
platform: "pc"
igdb: 2155
---

Possibly my favorite game ever, mostly for the PVP. I recognize that the game falls short in a lot of areas, especially the later post Anor Londo, but it's okay. I love it anyway.

Note: I did not put a finished date on this one since I've played it many many times over the years.
34 changes: 34 additions & 0 deletions src/content/games/dota2/_data.json
@@ -0,0 +1,34 @@
{
"first_release_date": 1373328000,
"genres": [
{
"id": 15,
"name": "Strategy"
},
{
"id": 36,
"name": "MOBA"
}
],
"platforms": [
{
"id": 3,
"abbreviation": "Linux"
},
{
"id": 6,
"abbreviation": "PC"
},
{
"id": 14,
"abbreviation": "Mac"
}
],
"companies": [
{
"id": 56,
"name": "Valve",
"role": "developer"
}
]
}
Binary file added src/content/games/dota2/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/content/games/dota2/dota2.md
@@ -0,0 +1,9 @@
---
title: "Dota 2"
rating: "masterpiece"
finishedDate: "N/A"
platform: "pc"
igdb: 2963
---

I don't play it anymore, mostly because I don't have much people to play it with, but I strongly believe Dota 2 to be one of the best designed games of all time, IceFrog is simply a genius.
44 changes: 44 additions & 0 deletions src/content/games/leagueoflegends/_data.json
@@ -0,0 +1,44 @@
{
"first_release_date": 1256601600,
"genres": [
{
"id": 12,
"name": "Role-playing (RPG)"
},
{
"id": 15,
"name": "Strategy"
},
{
"id": 36,
"name": "MOBA"
}
],
"platforms": [
{
"id": 6,
"abbreviation": "PC"
},
{
"id": 14,
"abbreviation": "Mac"
}
],
"companies": [
{
"id": 1494,
"name": "GOA Games Services Ltd.",
"role": "publisher"
},
{
"id": 41,
"name": "Riot Games",
"role": "developer"
},
{
"id": 42,
"name": "Tencent Holdings",
"role": "publisher"
}
]
}
Binary file added src/content/games/leagueoflegends/cover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/content/games/leagueoflegends/leagueoflegends.md
@@ -0,0 +1,9 @@
---
title: League of Legends
rating: "liked"
finishedDate: "N/A"
platform: "pc"
igdb: 115
---

Fun, most of the time.

1 comment on commit 2b08f05

@vercel
Copy link

@vercel vercel bot commented on 2b08f05 Jul 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

erika-florist – ./

erika-florist-git-main-princesseuh.vercel.app
erika-florist.vercel.app
erika-florist-princesseuh.vercel.app

Please sign in to comment.