Skip to content

Commit

Permalink
[CHORE] Commit start of a Developer Guide
Browse files Browse the repository at this point in the history
Since more and more different people work on and contribute to Hedy,
expecting new people to absorb architectural decisions from the existing
code is not tenable.

This change adds a start for a developer guide with content to guide
future implementors.
  • Loading branch information
rix0rrr committed Apr 16, 2023
1 parent 94d3618 commit a2876f3
Showing 1 changed file with 148 additions and 0 deletions.
148 changes: 148 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Hedy Developer Guide

This document describes conventions used in Hedy development. Read this document
if you are looking to learn more about how to implement new features, or how to
organize your code.

You may see existing code in the current code base that contradicts these
recommendations. Code may be in the wrong place, named incorrectly, or
implemented using non-recommended strategies. If the code has a good reason to
deviate from the advice given here, there will be a code comment explaining why.
Otherwise, the code is from before we wrote down this guidance, and it will be
refactored to match the guide in due time.

## Technologies

The technologies used in building Hedy are the following:

### Backend: Python, Flask, Jinja2, DynamoDB

The backend is a web server, implemented in Python using the Flask framework.
Data is stored in AWS DynamoDB.

The backend accesses the database and renders HTML pages in response to HTTP
requests. HTML pages are rendered using the templating framework Jinja2.

Resources:

* [Flask tutorial](https://flask.palletsprojects.com/en/2.2.x/tutorial/)
* [Jinja2 Template Designer Documentation](https://jinja.palletsprojects.com/en/3.1.x/templates/)


### Front-end: HTML, Tailwind, HTMX, HyperScript, TypeScript

The front-end is preferably plain HTML and CSS, with a minimal amount of
client-side code (written in TypeScript).

In-page interactivity is added by using either HTMX or HyperScript: HTMX does a
server request and an inline page update, and should be used if the server needs
to be involved in the interaction. HyperScript is like JavaScript but more
expressive, and can be used to add light interactivity to the page. For example:

- If clicking a button needs to read or write something to the database, use
HTMX.
- If clicking a button needs to show or hide some page element, or change the
style of some other element, use Hyperscript.

For more advanced use cases that cannot be solved using any of these existing
mechanism, new TypeScript can be added instead. However, this should rarely be
necessary.

For CSS, we use Tailwind. Tailwind is a utility-based class framework, which
means there are a lot of classes to set individual CSS properties. Usually, you
put those classes directly into the HTML. In case it's desirable or necessary,
a combination of styles can be made into a new class using a build step.

* [Tailwind class reference](https://tailwindcss.com/docs/border-radius)
* [HTMX Website](https://htmx.org), pay special attention to the [examples](https://htmx.org/examples/)
* [Hyperscript introduction](https://hyperscript.org/docs/)
* [TypeScript Documentation](https://www.typescriptlang.org/docs/), check the
**Get Started** column.

## Jinja2 Templates

The main source of how we control what ends up on the page is by using Jinja2
templates. Most Flask routes end in a `render_template('example.html',
my_variable=my_variable)` call, which would take the file
`templates/example.html`, substitute any variables in it (for example, using the
value `{{ my_variable }}`, and send it to the user's browser.

### Reducing code duplication in templates

Jinja2 has the following capabilities to reduce on the amount of copy/pasted
code:

- `{% for %}` loops. Use this if you need to display the same (or similar) HTML
elements a number of times, for each element in a collection. Even if the
collection is a fixed size, or the elements are slightly different each time
(for example, each has a different color), you can still use for loops. Have
the Python code that provides the variables for the template precalculate the
attributes that are different, or use the `cycle` function. For example, the
quiz and the front page both use loops, even though the elements are different
every time.
- `{% extends %}` and `{% block %}`s. Use this if multiple pages have the same
basic page structure, but have placeholders where different types of content
are injected. For example, all pages ultimately extend `layout.html`, which
includes the menu bar, the CSS and all scripts.
- `{% include %}`. Use this either to reuse small snippets of HTML across
multiple pages, or to separate out a bit of HTML to a different file for
better code organization and readability. Files that are designed to be
included (rather than used in a call to `render_template`). For example, the
quiz has `incl-question-progress.html`, which is used on multiple pages
to render the UI that indicates the current question number. Alternatively,
`menubar.html` is only included from one place, but by splitting it off into a
separate file the code for it is easy to find.
- `{% macro xyz(...) %}`. Macros are like function calls: they are a way to
define a paramaterized template fragment that can be instantiated multiple
times with different values. This is useful if you want to reduce duplication
but the reused code isn't significant enough to warrant its own file. Macros
can be defined in includable files to make libraries of reusable snippets
(if you are planning to go this route, try to explore simpler options first).
For example, `adventure-tabs.html` has a macro to render a tab, which gets
called multiple times with multiple arguments. `macros/stats-shared.html`
is a template designed to be included that defines a bunch of macros that are
used in the statistics pages.

#### Conventions in template organization

We use the following organization and naming conventions in the templates:

- There are a lot of files in this directory. To keep it organized, prefer
using a directory by feature or site area if possible.
- Template files that are intended to be included from other templates either
start with `incl-` or are in the `incl/` directory.
- Template files that are intended to be rendered from Python using
`render_template()`, but in response to an HTMX request so they don't render
a full HTML page, start with `hx-`.

## Where to put code

### Deciding between server and client

### How to organize code

## How to make common changes

### How to add hover effects

### How to add click effects, selection effects

### Adding new tables

### GET vs POST


## The database

### Adding new tables

### Querying

## Tailwind

## How to reduce Flask duplication

- `g`
- Scope of variables
- `session`
- preprocessor

0 comments on commit a2876f3

Please sign in to comment.