docs: document some of our project styles/conventions (#2816)

Spurred by the discussion to converge on using `.` to separate generated
targets, I
wrote down some of the conventions we've adopted.

---------

Co-authored-by: Ignas Anikevicius <240938+aignas@users.noreply.github.com>
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..26bb52f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# Set default charset
+[*]
+charset = utf-8
+
+# Line width
+[*]
+max_line_length = 100
+
+# 4 space indentation
+[*.{py,bzl}]
+indent_style = space
+indent_size = 4
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 17558e1..b087119 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -173,6 +173,55 @@
 :::
 ```
 
+## Style and idioms
+
+For the most part, we just accept whatever the code formatters do, so there
+isn't much style to enforce.
+
+Some miscellanous style, idioms, and conventions we have are:
+
+### Markdown/Sphinx Style
+
+* Use colons for prose sections of text, e.g. `:::{note}`, not backticks.
+* Use backticks for code blocks.
+* Max line length: 100.
+
+### BUILD/bzl Style
+
+* When a macro generates public targets, use a dot (`.`) to separate the
+  user-provided name from the generted name. e.g. `foo(name="x")` generates
+  `x.test`. The `.` is our convention to communicate that it's a generated
+  target, and thus one should look for `name="x"` when searching for the
+  definition.
+* The different build phases shouldn't load code that defines objects that
+  aren't valid for their phase. e.g.
+  * The bzlmod phase shouldn't load code defining regular rules or providers.
+  * The repository phase shouldn't load code defining module extensions, regular
+    rules, or providers.
+  * The loading phase shouldn't load code defining module extensions or
+    repository rules.
+  * Loading utility libraries or generic code is OK, but should strive to load
+    code that is usable for its phase. e.g. loading-phase code shouldn't
+    load utility code that is predominately only usable to the bzlmod phase.
+* Providers should be in their own files. This allows implementing a custom rule
+  that implements the provider without loading a specific implementation.
+* One rule per file is preferred, but not required. The goal is that defining an
+  e.g. library shouldn't incur loading all the code for binaries, tests,
+  packaging, etc; things that may be niche or uncommonly used.
+* Separate files should be used to expose public APIs. This ensures our public
+  API is well defined and prevents accidentally exposing a package-private
+  symbol as a public symbol.
+
+  :::{note}
+  The public API file's docstring becomes part of the user-facing docs. That
+  file's docstring must be used for module-level API documentation.
+  :::
+* Repository rules should have name ending in `_repo`. This helps distinguish
+  them from regular rules.
+* Each bzlmod extension, the "X" of `use_repo("//foo:foo.bzl", "X")` should be
+  in its own file. The path given in the `use_repo()` expression is the identity
+  Bazel uses and cannot be changed.
+
 ## Generated files
 
 Some checked-in files are generated and need to be updated when a new PR is