Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add articles documentation #406

Merged
merged 6 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions building/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,13 @@
"title": "Approaches",
"blurb": "Learn how to write approaches for exercises"
},
{
"uuid": "362853a0-dc4a-43ee-abbb-d4a14cb5b5bb",
"slug": "tracks/articles",
"path": "building/tracks/articles.md",
"title": "Articles",
"blurb": "Learn how to write articles for exercises"
},
{
"uuid": "8db530bb-e11b-4497-b088-5b4c997e09a2",
"slug": "tracks/presentation",
Expand Down
96 changes: 96 additions & 0 deletions building/configlet/lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,102 @@ The `config.json` file should have the following checks:
- The `"test_runner"` key is optional
- The `"test_runner"` value must be a boolean

### Rule: exercises/{concept|practice}/<slug>/.approaches/config.json is valid

- The file's presence is optional, unless there is a `introduction.md` or a sibling directory
- The file must be valid JSON
- The JSON root must be an object
- The `"introduction.authors"` key is optional
- The `"introduction.authors"` value must be an array
- The `"introduction.authors"` values must be non-blank strings¹
- The `"introduction.authors"` values must not have duplicates
- The `"introduction.authors"` values are treated case-insensitively
- If the `"introduction.authors"` array is non-empty, there must be a non-empty `introduction.md` file
- The `"introduction.contributors"` key is optional
- The `"introduction.contributors"` value must be an array
- The `"introduction.contributors"` values must be non-blank strings¹
- The `"introduction.contributors"` values must not have duplicates
- The `"introduction.contributors"` values are treated case-insensitively
- If the `"introduction.contributors"` array is non-empty, there must be a non-empty `introduction.md` file
- Users can only be listed in either the `"introduction.authors"` or `"introduction.contributors"` array (no overlap)
- The `"approaches"` key is optional, unless there is a sibling directory present (which contains the approach' files)
- The `"approaches"` value must be an array of objects
- The `"approaches[].uuid"` key is required
- The `"approaches[].uuid"` value must be a unique version 4 UUID string⁶
- The `"approaches[].uuid"` value for each concept must never change
- The `"approaches[].slug"` key is required
- The `"approaches[].slug"` value must be a kebab-case string² with length <= 255
- The `"approaches[].slug"` value must have a corresponding non-empty `<slug>/content.md` file
- The `"approaches[].slug"` value must have a corresponding non-empty `<slug>/snippet.txt` file
- The `"approaches[].name"` key is required
- The `"approaches[].name"` value must be a Title Case string³ with length <= 255
- The `"approaches[].blurb"` key is required
- The `"approaches[].blurb"` value must be a non-blank string¹ with length <= 350
- The `"approaches[].authors"` key is required
- The `"approaches[].authors"` value must be a non-empty array
- The `"approaches[].authors"` values must be non-blank strings¹
- The `"approaches[].authors"` values must not have duplicates
- The `"approaches[].authors"` values are treated case-insensitively
- The `"approaches[].contributors"` key is optional
- The `"approaches[].contributors"` value must be an array
- The `"approaches[].contributors"` values must be non-blank strings¹
- The `"approaches[].contributors"` values must not have duplicates
- The `"approaches[].contributors"` values are treated case-insensitively
- Users can only be listed in either the `"approaches[].authors"` or `"approaches[].contributors"` array (no overlap)

### Rule: exercises/{concept|practice}/&lt;slug&gt;/.approaches/&lt;approach-slug&gt;/content.md is valid

- The file's presence is required if a matching `"approaches[].slug"` entry exists in the `.approaches/config.json` file
- The Markdown must conform to the [Markdown standards](/docs/building/markdown/markdown)
- Links must be absolute (relative links are not allowed)

### Rule: exercises/{concept|practice}/&lt;slug&gt;/.approaches/&lt;approach-slug&gt;/snippet.txt is valid

- The file's presence is required if a matching `"approaches[].slug"` entry exists in the `.approaches/config.json` file
- The snippet must have at most 8 lines

### Rule: exercises/{concept|practice}/&lt;slug&gt;/.articles/config.json is valid

- The file's presence is optional, unless there is a sibling directory
- The file must be valid JSON
- The JSON root must be an object
- The `"articles"` key is optional, unless there is a sibling directory present (which contains the article' files)
- The `"articles"` value must be an array of objects
- The `"articles[].uuid"` key is required
- The `"articles[].uuid"` value must be a unique version 4 UUID string⁶
- The `"articles[].uuid"` value for each concept must never change
- The `"articles[].slug"` key is required
- The `"articles[].slug"` value must be a kebab-case string² with length <= 255
- The `"articles[].slug"` value must have a corresponding non-empty `<slug>/content.md` file
- The `"articles[].slug"` value must have a corresponding non-empty `<slug>/snippet.md` file
- The `"articles[].name"` key is required
- The `"articles[].name"` value must be a Title Case string³ with length <= 255
- The `"articles[].blurb"` key is required
- The `"articles[].blurb"` value must be a non-blank string¹ with length <= 350
- The `"articles[].authors"` key is required
- The `"articles[].authors"` value must be a non-empty array
- The `"articles[].authors"` values must be non-blank strings¹
- The `"articles[].authors"` values must not have duplicates
- The `"articles[].authors"` values are treated case-insensitively
- The `"articles[].contributors"` key is optional
- The `"articles[].contributors"` value must be an array
- The `"articles[].contributors"` values must be non-blank strings¹
- The `"articles[].contributors"` values must not have duplicates
- The `"articles[].contributors"` values are treated case-insensitively
- Users can only be listed in either the `"articles[].authors"` or `"articles[].contributors"` array (no overlap)

### Rule: exercises/{concept|practice}/&lt;slug&gt;/.articles/&lt;article-slug&gt;/content.md is valid

- The file's presence is required if a matching `"articles[].slug"` entry exists in the `.articles/config.json` file
- The Markdown must conform to the [Markdown standards](/docs/building/markdown/markdown)
- Links must be absolute (relative links are not allowed)

### Rule: exercises/{concept|practice}/&lt;slug&gt;/.articles/&lt;article-slug&gt;/snippet.md is valid

- The file's presence is required if a matching `"articles[].slug"` entry exists in the `.articles/config.json` file
- The Markdown must conform to the [Markdown standards](/docs/building/markdown/markdown)
- The snippet must have at most 8 lines (leading and trailing code fence markers are ignored)

### Rule: exercises/shared/.docs/debug.md is valid

- The file's presence is optional
Expand Down
9 changes: 7 additions & 2 deletions building/tracks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ Tracks have two types of exercises:
- Concept exercises: they are designed to teach one or more concepts to a student. Check the [documentation](/docs/building/tracks/concept-exercises) for more information.
- Practice exercises: they are designed to practice learned concepts. Check the [documentation](/docs/building/tracks/practice-exercises) for more information.

Exercises can have approaches associated with them, which describe the different ways in which an exercise can be solved.
Check the [documentation](/docs/building/tracks/approaches) for more information.
### Dig deeper

Each exercise has an optional Dig Deeper section that can contain:

- [Approaches](/docs/building/tracks/approaches): different ways in which the exercise can be solved
- [Articles](/docs/building/tracks/articles): describe interesting aspects of the exercise
- Community videos: videos that showcase the exercise, usually by having someone solve the exercise from scratch

## Shared files

Expand Down
3 changes: 2 additions & 1 deletion building/tracks/approaches.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ An approach should explore how an exercise can be solved a certain way.
- Its contents should either:
- Explore an idiomatic approach
- Explore a non-idiomatic, but interesting approach
- Contain a meta discussion (e.g. comparing the performance of approaches)
- Start with a (full) code sample
- Liberally use code samples in the rest of the document
- Feel free to dig deep into the topic
- Link to useful resources (e.g. documentation)
- The snippet should showcase the core of the approach
- A maximum of 8 lines can be used

If you'd like to compare different approaches, please write an [articles](/docs/building/tracks/articles).

## Approaches overview

- Give context to the problem
Expand Down
19 changes: 19 additions & 0 deletions building/tracks/articles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Articles

Each exercise can have articles associated with them, which explore some interesting aspect of the exercise.

## Topics

Potential topics an article could explore:

- Comparing the performance of different approaches
- Anything interesting you can come up with!

## General considerations

- If your article is based on some code you've written, consider committing that code (within the article directory)
- An example being an article on performance, for which benchmarking code was written

## What exercises to write articles for?

Any exercise, as long as there is something interesting to explore.
133 changes: 118 additions & 15 deletions building/tracks/concept-exercises.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ These files describe approaches for the exercise.
- `.approaches/<approach-slug>/content.md`: description of the approach (optional)
- `.approaches/<approach-slug>/snippet.txt`: snippet showcasing the approach (optional)

### Article files

These files describe articles for the exercise.

- `.articles/config.json`: metadata for the articles (optional)
- `.articles/<article-slug>/content.md`: description of the article (optional)
- `.articles/<article-slug>/snippet.md`: snippet showcasing the article (optional)

### Exercise files

The language-specific files, like the implementation and test files. The names of these files are track-specific.
Expand All @@ -72,11 +80,16 @@ exercises
└── concept
└── cars-assemble
├── .approaches
| ├── performance (approach)
| ├── for-loop
| | ├── content.md
| | └── snippet.txt
| ├── config.json
| └── introduction.md
├── .articles
| ├── performance
| | ├── content.md
| | └── snippet.md
| └── config.json
├── .docs
| ├── introduction.md
| ├── instructions.md
Expand Down Expand Up @@ -403,7 +416,7 @@ If readability is your primary concern (and it usually should be), the LINQ-base

**Presence:** Optional (required when an approach introduction or approach exists)

This file contains meta information on the exercise:
This file contains meta information on the exercise's approaches:

- `introduction`: The GitHub username(s) of the exercise approach introduction's author(s) (optional)

Expand Down Expand Up @@ -432,6 +445,96 @@ This file contains meta information on the exercise:
"approaches": [
{
"uuid": "448fb2b4-18ab-4e55-aa54-ad4ed6d5f7f6",
"slug": "span",
"title": "Use Span<T>",
"blurb": "Use Span<T> to efficiently reverse a string.",
"authors": ["erikschierboom"]
}
]
}
```

---

### File: `.approaches/<approach-slug>/content.md`

**Purpose:** Detailed description of the approach

**Presence:** Optional (required for approaches)

This file contains a detailed description of the approach.
Check the [documentation](/docs/building/tracks/approaches) for more information on what should go in this file.

#### Example

````markdown
# Span

```csharp
Span<char> chars = stackalloc char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
```

This `Span<T>` approach uses a `for` loop.
````

---

### File: `.approaches/<approach-slug>/snippet.txt`

**Purpose:** Snippet showcasing the approach

**Presence:** Optional (required for approaches)

This file contains a small snippet that showcases the approach.
The snippet is shown on an exercise's dig deeper page.

Its number of lines must be <= 8.

Check the [documentation](/docs/building/tracks/approaches) for more information on what should go in this file.

#### Example

```csharp
Span<char> chars = stackalloc char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
```

---

### File: `.article/config.json`

**Purpose:** Metadata for the articles

**Presence:** Optional (required when an article exists)

This file contains meta information on the exercise's articles:

- `articles`: An array listing the detailed articles (optional)
- `uuid`: a V4 UUID that uniquely identifies the article. The UUID must be unique both within the track as well as across all tracks, and must never change
- `slug`: the article's slug, which is a lowercased, kebab-case string. The slug must be unique across all article slugs within the track. Its length must be <= 255.
- `title`: the article's title. Its length must be <= 255.
- `blurb`: A short description of this article. Its length must be <= 350. Markdown is _not_ supported (required)
- `authors`: The GitHub username(s) of the exercise article's author(s) (required)
- Including reviewers if their reviews substantially change the exercise article (to the extent where it feels like "you got there together")
- `contributors`: The GitHub username(s) of the exercise article's contributor(s) (optional)
- Including reviewers if their reviews are meaningful/actionable/actioned.

#### Example

```json
{
"articles": [
{
"uuid": "6db71962-62d5-448b-a980-c20ae41013ed",
"slug": "performance",
"title": "Optimizing performance",
"blurb": "Explore how to most efficiently reverse a string and what the trade-offs are.",
Expand All @@ -443,14 +546,14 @@ This file contains meta information on the exercise:

---

### File: `.approaches/<approach-slug>/content.md`
### File: `.articles/<article-slug>/content.md`

**Purpose:** Detailed description of the approach

**Presence:** Optional (required for approaches)

This file contains a detailed description of the approach.
Check the [documentation](/docs/building/tracks/approaches) for more information on what should go in this file.
Check the [documentation](/docs/building/tracks/articles) for more information on what should go in this file.

#### Example

Expand All @@ -469,26 +572,26 @@ In this document, we'll find out which approach is the most performant one.

---

### File: `.approaches/<approach-slug>/snippet.txt`
### File: `.articles/<article-slug>/snippet.txt`

**Purpose:** Snippet showcasing the approach

**Presence:** Optional (required for approaches)
**Presence:** Optional (required for articles)

This file contains a small snippet that showcases the approach.
The snippet is shown on an exercise's approaches overview page.
This file contains a small snippet that showcases the article.
The snippet is shown on an exercise's dig deeper page.

Its number of lines must be <= 8.

Check the [documentation](/docs/building/tracks/articles) for more information on what should go in this file.

#### Example

```csharp
Span<char> chars = stackalloc char[input.Length];
for (var i = 0; i < input.Length; i++)
{
chars[input.Length - 1 - i] = input[i];
}
return new string(chars);
```markdown
| Method | Mean | Allocated |
| -----: | --------: | --------: |
| Linq | 29.133 ns | 80 B |
| Array | 4.806 ns | - |
```

---
Expand Down
Loading