From eb2d14ba98b43a966cba8536a584a786f7ab1cb5 Mon Sep 17 00:00:00 2001
From: Nicole Dresselhaus
Date: Sun, 29 Jun 2025 13:46:55 +0200
Subject: [PATCH] fixups in formatting; minor other stuff.
---
Coding/Haskell/FFPiH.md | 7 +
Coding/Haskell/Webapp-Example/index.qmd | 4 -
Writing/coding-age-ai.md | 117 ++-
dist/Coding/Haskell/FFPiH.html | 2 +-
dist/Coding/Haskell/Webapp-Example/index.html | 2 -
dist/Writing/coding-age-ai.html | 421 ++++----
dist/index.html | 66 +-
dist/index.xml | 627 +++++++-----
dist/listings.json | 2 +-
dist/search.json | 946 +++++++++---------
dist/sitemap.xml | 24 +-
...b0wtewyr9wthgg6e8agt_1750971350_img_0.webp | Bin 37682 -> 0 bytes
index.qmd | 4 +
13 files changed, 1186 insertions(+), 1036 deletions(-)
delete mode 100644 dist/thumbs/assets_task_01jyq0b0wtewyr9wthgg6e8agt_1750971350_img_0.webp
diff --git a/Coding/Haskell/FFPiH.md b/Coding/Haskell/FFPiH.md
index 8f25ac6..ce20f7c 100644
--- a/Coding/Haskell/FFPiH.md
+++ b/Coding/Haskell/FFPiH.md
@@ -7,6 +7,13 @@ categories:
- Haskell
date: 2018-01-01
title: Fortgeschrittene funktionale Programmierung in Haskell
+format:
+ html:
+ other-links:
+ - text: FFPiH-Organisation auf Gitea
+ icon: cup-hot-fill
+ href: "https://gitea.dresselhaus.cloud/FFPiH"
+ rel: me
---
FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015
diff --git a/Coding/Haskell/Webapp-Example/index.qmd b/Coding/Haskell/Webapp-Example/index.qmd
index 8f2417f..64ea60e 100644
--- a/Coding/Haskell/Webapp-Example/index.qmd
+++ b/Coding/Haskell/Webapp-Example/index.qmd
@@ -374,8 +374,6 @@ loggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutput
```
-{{< dend >}}
-
#### Weitere Instanzen und Definitionen, die der Generator (noch) nicht macht
In der `Myservice.Types` werden ein paar hilfreiche Typen und Typ-Instanzen
@@ -480,8 +478,6 @@ instance Out Response
instance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).
```
-{{< dend >}}
-
#### Was noch zu tun ist
Den Service implementieren. Einfach ein neues Modul aufmachen (z.B.
diff --git a/Writing/coding-age-ai.md b/Writing/coding-age-ai.md
index 02e60c9..4420d4d 100644
--- a/Writing/coding-age-ai.md
+++ b/Writing/coding-age-ai.md
@@ -50,12 +50,12 @@ format:
## Introduction
-In the fast eveolving field of AI there is a clear lack of reports on "what
+In the fast evolving field of AI there is a clear lack of reports on "what
really works". Some techniques hailed as revolution (like the DeepSeek
Aha-Moment[@DeepSeek-AI2025DeepSeekR1IncentivizingReasoning]) for unlimited
-potential were soon realized to "just" optimize nieche problems that can
+potential were soon realized to "just" optimize niche problems that can
benchmarked[@Shao2025SpuriousRewardsRethinking]^[Like all decent humans i ain't
-got time to read up on everything - so a big shoutout to
+got time to read up on everything - so a big shoot-out to
[@bycloud2025LLMsRLRevelation] for doing thorough reviews on ML-topics and
linking the respective papers!].
@@ -74,7 +74,7 @@ papers tested their finding on (qwen-series) also gets better with
RLVR-optimization if rewards are random instead of verified].
Therefore see this "Field Guide" for what it is: A current state of things that
-work for at least 1 individuum in exactly this ecosystem at this point in time.
+work for at least 1 Individuum in exactly this ecosystem at this point in time.
## How to program with Cursor
@@ -83,7 +83,7 @@ In essence [Cursor](https://cursor.com) is "just" a fork of
functionality: Automatically injecting files into LLM-Prompts, offering
tool-aware LLMs to use [MCP](https://modelcontextprotocol.io/introduction)s,
read the filesystem, execute arbitrary commands in the shell (either
-automomatically or after permission), getting feedback from the editor (i.e.
+automatically or after permission), getting feedback from the editor (i.e.
installed linters, language-servers etc.) and thus have the same (or even
better) information/tools available as the programmer in front of the screen.
@@ -97,10 +97,10 @@ they want - it can't be proven and (especially under US-Law) is not even
possible to resist lawful orders (including the gag-order to not talk about
these).
-In practise one feels the direct pain points more severly. Some regular examples
-include generating redundant code, because the current context was not aware of
-utility-modules and functions it could use - leading to huge technical debt in
-no time.
+In practise one feels the direct pain points more severely. Some regular
+examples include generating redundant code, because the current context was not
+aware of utility-modules and functions it could use - leading to huge technical
+debt in no time.
Therefore my preferred workflow is to "think bigger". Imagine being a product
owner of a huge, sluggish company. The left hand never knows what the right hand
@@ -137,9 +137,9 @@ The main theme always follows a similar pattern:
what is _out of scope_.
- Pin the desired behaviour in a **Specification**.
Either this means changing currently established specifications (i.e.
- bug/chang) or writing complete new ones (i.e. feature).
+ bug/change) or writing complete new ones (i.e. feature).
- Investigate **Spec-Compliance**.
- Again the agentlooks at the codebase to identify _where_ things should change
+ Again the agent looks at the codebase to identify _where_ things should change
and _how_. Also recommendation are made on how it could achieve the goal.
- Generate **Tasks**.
From the compliance-report of spec-deviations (either from a bug or from a
@@ -231,11 +231,27 @@ one‑sentence feature description plus any additional Q&A with the stakeholder.
### Output
-• Create /tasks//PRD.md • Markdown only – no prose, no code‑fences. •
-File structure: # ## 1. Problem / Motivation ## 2. Goals ## 3.
-Non‑Goals ## 4. Target Users & Personas ## 5. User Stories (Gherkin
-“Given/When/Then”) ## 6. Acceptance Criteria ## 7. Technical Notes /
-Dependencies ## 8. Open Questions
+- Create /tasks//PRD.md
+- Markdown only – no prose, no code‑fences.
+- File structure:
+
+ > #
+ >
+ > ## 1. Problem / Motivation
+ >
+ > ## 2. Goals
+ >
+ > ## 3. Non‑Goals
+ >
+ > ## 4. Target Users & Personas
+ >
+ > ## 5. User Stories (Gherkin “Given/When/Then”)
+ >
+ > ## 6. Acceptance Criteria
+ >
+ > ## 7. Technical Notes / Dependencies
+ >
+ > ## 8. Open Questions
### Process
@@ -247,23 +263,26 @@ Dependencies ## 8. Open Questions
ask for further clarification up to 3 times following this schema, else flag
it in _Open Questions_.
4. After questions are answered reply exactly: Ready to generate the PRD.
-5. On a user message that contains only the word "go" (case‑insensitive): •
- Generate /tasks//PRD.md following _Output_ spec. • Reply:
- /PRD.md created – review it.
+5. On a user message that contains only the word "go" (case‑insensitive):
+ - Generate /tasks//PRD.md following _Output_ spec.
+ - Reply: /PRD.md created – review it.
+
6. STOP. Do **not** generate tasks or code in this rule.
### Writing guidelines
-• Keep each bullet ≤120 characters. • Use action verbs and measurable language.
-• Leave TBDs only in _Open Questions_. • No business fluff – pretend the reader
-is a junior developer.
+- Keep each bullet ≤120 characters.
+- Use action verbs and measurable language.
+- Leave TBDs only in _Open Questions_.
+- No business fluff – pretend the reader is a junior developer.
### Safety rails
-• Assume all work happens in a non‑production environment, unless otherwise
-stated or requested by you. • Do not include sensitive data or credentials in
-the PRD. • Check the generated Document with `markdownlint` (if available),
-apply auto-fixes and fix the remaining issues manually.
+- Assume all work happens in a non‑production environment, unless otherwise
+ stated or requested by you.
+- Do not include sensitive data or credentials in the PRD.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
```
:::
@@ -305,19 +324,16 @@ Every specification should include:
```
2. **Scope and Boundaries**
-
- What is included/excluded
- Dependencies on other specifications
- Relationship to other components
3. **Detailed Requirements**
-
- Structured by logical sections
- Clear, unambiguous language
- Examples where helpful
4. **Error Handling**
-
- How errors should be handled
- Fallback behaviors
- Edge cases
@@ -614,12 +630,13 @@ list that a junior developer (human or AI) can follow without extra context.
### Output
-• Create /tasks//TASKS.md (overwrite if it exists). • Markdown only, no
-prose around it. • Epics = H2 headings (`## 1. `). • Tasks = unchecked
-check‑boxes (`- [ ] 1.1 `). • Sub‑tasks = indent one space under their
-parent (` - [ ] 1.1.1 `). • Create a
-/tasks//Task**\_.md (i.e. `Task_3_2_4.md` for Epic
-3, Task 2, Subtask 4)
+- Create /tasks//TASKS.md (overwrite if it exists).
+- Markdown only, no prose around it.
+- Epics = H2 headings (`## 1. `).
+- Tasks = unchecked check‑boxes (`- [ ] 1.1 `).
+- Sub‑tasks = indent one space under their parent (` - [ ] 1.1.1 `).
+- Create a /tasks//Task**\_.md (i.e.
+ `Task_3_2_4.md` for Epic 3, Task 2, Subtask 4)
### Process
@@ -644,20 +661,22 @@ parent (` - [ ] 1.1.1 `). • Create a
### Writing guidelines
-• Each item ≤120 characters, start with an action verb. • Hints are allowed
-below each item as HTML-Comment and do not count against the 120 characters. •
-Group related work into logical epics with ≤7 direct child items. • Prefer
-concrete file paths, commands, specs or APIs when available. • Skip
-implementation details obvious from the codebase in the overview. • If a task
-only concerns up to 5 files, name them in the detailed file. Otherwise give
-hints on how to search for them (i.e. "everything under src/models/").
+- Each item ≤120 characters, start with an action verb.
+- Hints are allowed below each item as HTML-Comment and do not count against the
+ 120 characters.
+- Group related work into logical epics with ≤7 direct child items.
+- Prefer concrete file paths, commands, specs or APIs when available.
+- Skip implementation details obvious from the codebase in the overview.
+- If a task only concerns up to 5 files, name them in the detailed file.
+ Otherwise give hints on how to search for them (i.e. "everything under
+ `src/models/`").
### Safety rails
-• Never touch production data. • Assume all work happens in a feature branch,
-never commit directly to main. • Check the generated Document with
-`markdownlint` (if available), apply auto-fixes and fix the remaining issues
-manually.
+- Never touch production data.
+- Assume all work happens in a feature branch, never commit directly to main.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
```
:::
@@ -675,9 +694,9 @@ specialised coding-LLMs are enough to get the job done with this preparation.
## Example: Rules in Action
The codebase we look at here is a project called `gitlab_overviewer`. It takes
-gitlab-api-keys and generates nice overviews for tracking metadata in different
+GitLab-api-keys and generates nice overviews for tracking metadata in different
projects across different groups. With a nice export to markdown (for rendering
-in gitlab itself) and quarto (for exporting to i.e. confluence) with multiple
+in GitLab itself) and quarto (for exporting to i.e. confluence) with multiple
pages etc. pp.
The current issue is, that due to a complete rewrite we are happy with the
@@ -1225,7 +1244,7 @@ this method works and _can_ yields great outcomes. Even small discrepancies in
the codebase tend to pop up during spec-reviews (which can be automated!). Next
up would be running those in some kind of CI-job and integrating tools like
issue-tracking into the agent instead of simple markdown-files in the repository
-as makeshift issue-tracker. But not by me for the forseeable future, so if you
+as makeshift issue-tracker. But not by me for the foreseeable future, so if you
are looking for a project, feel free!
**All in all this isn't a silver bullet for all AI-assisted development
diff --git a/dist/Coding/Haskell/FFPiH.html b/dist/Coding/Haskell/FFPiH.html
index a96121f..f16f429 100644
--- a/dist/Coding/Haskell/FFPiH.html
+++ b/dist/Coding/Haskell/FFPiH.html
@@ -587,7 +587,7 @@ ul.task-list li input[type="checkbox"] {
Weitere Instanzen und Definitionen, die der Generator (noch) nicht macht
@@ -1047,7 +1046,6 @@ pre > code.sourceCode > span > a:first-child::before { text-decoration: underlin
instanceOutResponseinstanceFromBSONRepsonse-- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).
-{{< dend >}}
In the fast eveolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize nieche problems that can benchmarked[2]1.
-
1 Like all decent humans i ain’t got time to read up on everything - so a big shoutout to [3] for doing thorough reviews on ML-topics and linking the respective papers!
2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verified
I personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.
-
Therefore see this “Field Guide” for what it is: A current state of things that work for at least 1 individuum in exactly this ecosystem at this point in time.
+
In the fast evolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize niche problems that can benchmarked[2]1.
+
1 Like all decent humans i ain’t got time to read up on everything - so a big shoot-out to [3] for doing thorough reviews on ML-topics and linking the respective papers!
2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verified
I personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.
+
Therefore see this “Field Guide” for what it is: A current state of things that work for at least 1 Individuum in exactly this ecosystem at this point in time.
How to program with Cursor
-
In essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automomatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.
+
In essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.
Capabilities / General procedure
The main issue is now: theoretically agentic IDEs can get all information - practically it is limited directly by token-window sizes, costs of these queries; and indirectly by outsourced costs like environmental impacts, data-security, etc. The suppliers of such services can claim privacy as much as they want - it can’t be proven and (especially under US-Law) is not even possible to resist lawful orders (including the gag-order to not talk about these).
-
In practise one feels the direct pain points more severly. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.
+
In practise one feels the direct pain points more severely. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.
Therefore my preferred workflow is to “think bigger”. Imagine being a product owner of a huge, sluggish company. The left hand never knows what the right hand does or has done (i.e. the LLM forgetting things already exist in the codebase), everything has to be rigorous defined, specified and enforced. Some people reported good results with Test-Driven-Development (TDD) - but in my experience these things only prevent regressions and not proactively enforce the desired agent behaviour.
@@ -764,9 +764,9 @@ This could be a bug, a feature, some changes to existing behaviour etc.
This dives into the codebase to asses the current state of things. Maybe some bugs are obvious and easily fixed.
This formalizes that the LLM understood what should be done and especially what is out of scope.
Pin the desired behaviour in a Specification.
-Either this means changing currently established specifications (i.e. bug/chang) or writing complete new ones (i.e. feature).
+Either this means changing currently established specifications (i.e. bug/change) or writing complete new ones (i.e. feature).
Investigate Spec-Compliance.
-Again the agentlooks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.
+Again the agent looks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.
Generate Tasks.
From the compliance-report of spec-deviations (either from a bug or from a new/changed spec) finally a Plan to fix everything can be derived (think: Sprint-Planning).
NOTE: Up to here the agent never touched the code.
@@ -839,39 +839,58 @@ This is most often the most trivial step. Everything is known and formulated for
### Output
-• Create /tasks/<feature>/PRD.md • Markdown only – no prose, no code‑fences. •
-File structure: # <Feature title> ## 1. Problem / Motivation ## 2. Goals ## 3.
-Non‑Goals ## 4. Target Users & Personas ## 5. User Stories (Gherkin
-“Given/When/Then”) ## 6. Acceptance Criteria ## 7. Technical Notes /
-Dependencies ## 8. Open Questions
-
-### Process
-
-1. Stakeholder provides a single‑sentence feature idea and invokes this rule.
-2. Look at specifications in `specs/` and inspect the code if needed to get an
- idea what the Stakeholder expects from this feature.
-3. Ask up to five clarifying questions (Q1 … Q5). If anything is still vague
- after five, look into the project with the new information provided. You may
- ask for further clarification up to 3 times following this schema, else flag
- it in _Open Questions_.
-4. After questions are answered reply exactly: Ready to generate the PRD.
-5. On a user message that contains only the word "go" (case‑insensitive): •
- Generate /tasks/<feature>/PRD.md following _Output_ spec. • Reply:
-<feature>/PRD.md created – review it.
-6. STOP. Do **not** generate tasks or code in this rule.
-
-### Writing guidelines
-
-• Keep each bullet ≤120 characters. • Use action verbs and measurable language.
-• Leave TBDs only in _Open Questions_. • No business fluff – pretend the reader
-is a junior developer.
-
-### Safety rails
-
-• Assume all work happens in a non‑production environment, unless otherwise
-stated or requested by you. • Do not include sensitive data or credentials in
-the PRD. • Check the generated Document with `markdownlint` (if available),
-apply auto-fixes and fix the remaining issues manually.
+- Create /tasks/<feature>/PRD.md
+- Markdown only – no prose, no code‑fences.
+- File structure:
+
+ > # <Feature title>
+ >
+ > ## 1. Problem / Motivation
+ >
+ > ## 2. Goals
+ >
+ > ## 3. Non‑Goals
+ >
+ > ## 4. Target Users & Personas
+ >
+ > ## 5. User Stories (Gherkin “Given/When/Then”)
+ >
+ > ## 6. Acceptance Criteria
+ >
+ > ## 7. Technical Notes / Dependencies
+ >
+ > ## 8. Open Questions
+
+### Process
+
+1. Stakeholder provides a single‑sentence feature idea and invokes this rule.
+2. Look at specifications in `specs/` and inspect the code if needed to get an
+ idea what the Stakeholder expects from this feature.
+3. Ask up to five clarifying questions (Q1 … Q5). If anything is still vague
+ after five, look into the project with the new information provided. You may
+ ask for further clarification up to 3 times following this schema, else flag
+ it in _Open Questions_.
+4. After questions are answered reply exactly: Ready to generate the PRD.
+5. On a user message that contains only the word "go" (case‑insensitive):
+ - Generate /tasks/<feature>/PRD.md following _Output_ spec.
+ - Reply: <feature>/PRD.md created – review it.
+
+6. STOP. Do **not** generate tasks or code in this rule.
+
+### Writing guidelines
+
+- Keep each bullet ≤120 characters.
+- Use action verbs and measurable language.
+- Leave TBDs only in _Open Questions_.
+- No business fluff – pretend the reader is a junior developer.
+
+### Safety rails
+
+- Assume all work happens in a non‑production environment, unless otherwise
+ stated or requested by you.
+- Do not include sensitive data or credentials in the PRD.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
A call to this rule usually looks like @generate-prd We noticed, that …. Therefore investigate the codebase to come up with a PRD addressing these issues..
@@ -907,121 +926,118 @@ This is most often the most trivial step. Everything is known and formulated for
```2. **Scope and Boundaries**
-
- - What is included/excluded
- - Dependencies on other specifications
- - Relationship to other components
-
-3. **Detailed Requirements**
-
- - Structured by logical sections
- - Clear, unambiguous language
- - Examples where helpful
-
-4. **Error Handling**
-
- - How errors should be handled
- - Fallback behaviors
- - Edge cases
-
-5. **Testing Requirements**
- - Expected test coverage
- - Snapshot requirements
- - Approval test criteria
-
-## Writing Standards
-
-### Clarity and Precision
-
-- **Use specific language**: Avoid vague terms like "should" or "might"
-- **Provide examples**: Include concrete examples for complex requirements
-- **Define terms**: Clearly define any technical terms or concepts
-- **Use consistent formatting**: Follow established patterns from existing specs
-
-### Structure and Organization
-
-- **Logical flow**: Organize sections in logical order
-- **Consistent headings**: Use consistent heading levels and naming
-- **Cross-references**: Link to related specifications using
-`[spec_name](mdc:specs/spec_name.md)`
-- **Code blocks**: Use appropriate language tags for code examples
-
-### Completeness
-
-- **Cover all cases**: Address normal, error, and edge cases
-- **Be exhaustive**: Don't assume implementation details
-- **Consider interactions**: How this spec relates to others
-- **Future-proof**: Consider potential changes and extensions
-
-## Specification Maintenance
-
-### Version Control
-
-- **Update specs first**: When changing behavior, update spec before
- implementation
-- **Document changes**: Use clear commit messages explaining spec changes
-- **Review process**: Have specs reviewed before implementation
-
-### Consistency Checks
-
-- **Cross-reference validation**: Ensure all links to other specs are valid
-- **Terminology consistency**: Use consistent terms across all specs
-- **Format consistency**: Follow established formatting patterns
-
-### Testing Integration
-
-- **Spec-driven tests**: Write tests based on specification requirements
-- **Snapshot validation**: Ensure snapshots match specification exactly
-- **Approval tests**: Use approval tests to catch spec violations
-
-## Quality Checklist
-
-### Before Finalizing Specification
-
-- [ ] All requirements clearly stated
-- [ ] Examples provided for complex requirements
-- [ ] Error cases covered
-- [ ] Cross-references to other specs included
-- [ ] Out of scope items clearly defined
-- [ ] Testing requirements specified
-- [ ] Consistent formatting throughout
-- [ ] Check the generated Document with `markdownlint` (if available), apply
- auto-fixes and fix the remaining issues manually.
-
-### Review Criteria
-
-- [ ] Is the specification unambiguous?
-- [ ] Are all edge cases covered?
-- [ ] Does it integrate well with other specs?
-- [ ] Is it testable?
-- [ ] Is it maintainable?
-
-## Common Pitfalls to Avoid
-
-### Ambiguity
-
-- **Vague language**: "The system should handle errors gracefully"
-- **Missing details**: Not specifying exact error handling behavior
-- **Unclear relationships**: Not explaining how components interact
-
-### Inconsistency
-
-- **Different terms**: Using different terms for the same concept
-- **Inconsistent formatting**: Not following established patterns
-- **Conflicting requirements**: Requirements that contradict other specs
-
-### Incompleteness
-
-- **Missing edge cases**: Not considering unusual scenarios
-- **Incomplete examples**: Examples that don't cover all cases
-- **Missing error handling**: Not specifying what happens when things go wrong
-
-## Related Rules
-
-- [spec-compliance-investigation.mdc](mdc:.cursor/rules/spec-compliance-investigation.mdc)
- How to investigate spec-implementation discrepancies
-- [base_overview.mdc](mdc:.cursor/rules/base_overview.mdc) Project structure and
- conventions
+ - What is included/excluded
+ - Dependencies on other specifications
+ - Relationship to other components
+
+3. **Detailed Requirements**
+ - Structured by logical sections
+ - Clear, unambiguous language
+ - Examples where helpful
+
+4. **Error Handling**
+ - How errors should be handled
+ - Fallback behaviors
+ - Edge cases
+
+5. **Testing Requirements**
+ - Expected test coverage
+ - Snapshot requirements
+ - Approval test criteria
+
+## Writing Standards
+
+### Clarity and Precision
+
+- **Use specific language**: Avoid vague terms like "should" or "might"
+- **Provide examples**: Include concrete examples for complex requirements
+- **Define terms**: Clearly define any technical terms or concepts
+- **Use consistent formatting**: Follow established patterns from existing specs
+
+### Structure and Organization
+
+- **Logical flow**: Organize sections in logical order
+- **Consistent headings**: Use consistent heading levels and naming
+- **Cross-references**: Link to related specifications using
+`[spec_name](mdc:specs/spec_name.md)`
+- **Code blocks**: Use appropriate language tags for code examples
+
+### Completeness
+
+- **Cover all cases**: Address normal, error, and edge cases
+- **Be exhaustive**: Don't assume implementation details
+- **Consider interactions**: How this spec relates to others
+- **Future-proof**: Consider potential changes and extensions
+
+## Specification Maintenance
+
+### Version Control
+
+- **Update specs first**: When changing behavior, update spec before
+ implementation
+- **Document changes**: Use clear commit messages explaining spec changes
+- **Review process**: Have specs reviewed before implementation
+
+### Consistency Checks
+
+- **Cross-reference validation**: Ensure all links to other specs are valid
+- **Terminology consistency**: Use consistent terms across all specs
+- **Format consistency**: Follow established formatting patterns
+
+### Testing Integration
+
+- **Spec-driven tests**: Write tests based on specification requirements
+- **Snapshot validation**: Ensure snapshots match specification exactly
+- **Approval tests**: Use approval tests to catch spec violations
+
+## Quality Checklist
+
+### Before Finalizing Specification
+
+- [ ] All requirements clearly stated
+- [ ] Examples provided for complex requirements
+- [ ] Error cases covered
+- [ ] Cross-references to other specs included
+- [ ] Out of scope items clearly defined
+- [ ] Testing requirements specified
+- [ ] Consistent formatting throughout
+- [ ] Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
+
+### Review Criteria
+
+- [ ] Is the specification unambiguous?
+- [ ] Are all edge cases covered?
+- [ ] Does it integrate well with other specs?
+- [ ] Is it testable?
+- [ ] Is it maintainable?
+
+## Common Pitfalls to Avoid
+
+### Ambiguity
+
+- **Vague language**: "The system should handle errors gracefully"
+- **Missing details**: Not specifying exact error handling behavior
+- **Unclear relationships**: Not explaining how components interact
+
+### Inconsistency
+
+- **Different terms**: Using different terms for the same concept
+- **Inconsistent formatting**: Not following established patterns
+- **Conflicting requirements**: Requirements that contradict other specs
+
+### Incompleteness
+
+- **Missing edge cases**: Not considering unusual scenarios
+- **Incomplete examples**: Examples that don't cover all cases
+- **Missing error handling**: Not specifying what happens when things go wrong
+
+## Related Rules
+
+- [spec-compliance-investigation.mdc](mdc:.cursor/rules/spec-compliance-investigation.mdc)
+ How to investigate spec-implementation discrepancies
+- [base_overview.mdc](mdc:.cursor/rules/base_overview.mdc) Project structure and
+ conventions
As it is obvious this is a very intricate rule with many criteria. For this you really need a reasoning and deep-thinking model that can also reason for extended times (many minutes are normal!) and call tools every now and then to get even more information. Models like o3, deepseek-r1 and the opus-series of claude really shine here.
@@ -1201,50 +1217,53 @@ This is most often the most trivial step. Everything is known and formulated for
### Output
-• Create /tasks/<feature>/TASKS.md (overwrite if it exists). • Markdown only, no
-prose around it. • Epics = H2 headings (`## 1. <Epic>`). • Tasks = unchecked
-check‑boxes (`- [ ] 1.1 <task>`). • Sub‑tasks = indent one space under their
-parent (` - [ ] 1.1.1 <subtask>`). • Create a
-/tasks/<feature>/Task*<Epic>*<task>\_<subtask>.md (i.e. `Task_3_2_4.md` for Epic
-3, Task 2, Subtask 4)
-
-### Process
-
-1. Read the tagged PRD.
-2. **Investigate** the current state of the repository to collect answers to
- your first questions. All specs for fixed behaviours and outputs are located
- in `specs/`. **Consult those** as a source first before trying to
- reverse-engineer from the code.
- If specs are in need of change then this is also a task to be generated.
-3. If critical info is missing and cannot be answered by looking at the code,
- ask max five clarifying questions (Q1 … Q5) and stop until answered.
-4. After questions are answered think about the answers and: Either: look at the
- code again, then goto 3., and ask for further clarification Or: Reply
- exactly: Ready to generate the subtasks – respond **go** to proceed.
-5. On a user message that contains only the word "go" (case‑insensitive): a.
- Generate /tasks/<feature>/TASKS.md following _Output_ spec. b. Reply with:
- TASKS.md created – review them.
-6. After TASKS.md was reviewed, create `Task_<e>_<t>_<s>.md` for each task and
- subtask containing implementation hints like relevant specs (link them!),
- primary files to edit/review for this task, tests needing change, etc.
-7. Stop. Do **not** begin executing tasks in this rule.
-
-### Writing guidelines
-
-• Each item ≤120 characters, start with an action verb. • Hints are allowed
-below each item as HTML-Comment and do not count against the 120 characters. •
-Group related work into logical epics with ≤7 direct child items. • Prefer
-concrete file paths, commands, specs or APIs when available. • Skip
-implementation details obvious from the codebase in the overview. • If a task
-only concerns up to 5 files, name them in the detailed file. Otherwise give
-hints on how to search for them (i.e. "everything under src/models/").
-
-### Safety rails
-
-• Never touch production data. • Assume all work happens in a feature branch,
-never commit directly to main. • Check the generated Document with
-`markdownlint` (if available), apply auto-fixes and fix the remaining issues
-manually.
+- Create /tasks/<feature>/TASKS.md (overwrite if it exists).
+- Markdown only, no prose around it.
+- Epics = H2 headings (`## 1. <Epic>`).
+- Tasks = unchecked check‑boxes (`- [ ] 1.1 <task>`).
+- Sub‑tasks = indent one space under their parent (` - [ ] 1.1.1 <subtask>`).
+- Create a /tasks/<feature>/Task*<Epic>*<task>\_<subtask>.md (i.e.
+`Task_3_2_4.md` for Epic 3, Task 2, Subtask 4)
+
+### Process
+
+1. Read the tagged PRD.
+2. **Investigate** the current state of the repository to collect answers to
+ your first questions. All specs for fixed behaviours and outputs are located
+ in `specs/`. **Consult those** as a source first before trying to
+ reverse-engineer from the code.
+ If specs are in need of change then this is also a task to be generated.
+3. If critical info is missing and cannot be answered by looking at the code,
+ ask max five clarifying questions (Q1 … Q5) and stop until answered.
+4. After questions are answered think about the answers and: Either: look at the
+ code again, then goto 3., and ask for further clarification Or: Reply
+ exactly: Ready to generate the subtasks – respond **go** to proceed.
+5. On a user message that contains only the word "go" (case‑insensitive): a.
+ Generate /tasks/<feature>/TASKS.md following _Output_ spec. b. Reply with:
+ TASKS.md created – review them.
+6. After TASKS.md was reviewed, create `Task_<e>_<t>_<s>.md` for each task and
+ subtask containing implementation hints like relevant specs (link them!),
+ primary files to edit/review for this task, tests needing change, etc.
+7. Stop. Do **not** begin executing tasks in this rule.
+
+### Writing guidelines
+
+- Each item ≤120 characters, start with an action verb.
+- Hints are allowed below each item as HTML-Comment and do not count against the
+ 120 characters.
+- Group related work into logical epics with ≤7 direct child items.
+- Prefer concrete file paths, commands, specs or APIs when available.
+- Skip implementation details obvious from the codebase in the overview.
+- If a task only concerns up to 5 files, name them in the detailed file.
+ Otherwise give hints on how to search for them (i.e. "everything under
+`src/models/`").
+
+### Safety rails
+
+- Never touch production data.
+- Assume all work happens in a feature branch, never commit directly to main.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
This also works better with one of those deep-thinking models.
@@ -1256,7 +1275,7 @@ This is most often the most trivial step. Everything is known and formulated for
Example: Rules in Action
-
The codebase we look at here is a project called gitlab_overviewer. It takes gitlab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in gitlab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.
+
The codebase we look at here is a project called gitlab_overviewer. It takes GitLab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in GitLab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.
The current issue is, that due to a complete rewrite we are happy with the current output, but there are some minor problems still remaining. Some of them may also due to specs being incorrect, because requirements or ideas changed over time.
Analyzing the situation
@@ -1728,7 +1747,7 @@ Cursor
Project Integrity: Through rigorous spec adherence and systematic task breakdown, the codebase is more consistent, much better documented and decently tested5.
Reduced Cognitive Load: The Cursor rule system has made it easier to focus on actual development tasks instead of wrestling with the AI’s sometimes questionable assumptions - or just installing a third different testing framework for exactly this module.
-
5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.
In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the forseeable future, so if you are looking for a project, feel free!
+
5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.
In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the foreseeable future, so if you are looking for a project, feel free!
All in all this isn’t a silver bullet for all AI-assisted development problems, but it’s made my coding experience with Cursor much more productive and predictable. It turns out treating an AI as a slightly overeager junior developer who needs clear instructions works better than hoping it’ll just “do the right thing”.
In essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automomatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.
+
In essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.
Capabilities / General procedure
The main issue is now: theoretically agentic IDEs can get all information - practically it is limited directly by token-window sizes, costs of these queries; and indirectly by outsourced costs like environmental impacts, data-security, etc. The suppliers of such services can claim privacy as much as they want - it can’t be proven and (especially under US-Law) is not even possible to resist lawful orders (including the gag-order to not talk about these).
-
In practise one feels the direct pain points more severly. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.
+
In practise one feels the direct pain points more severely. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.
Therefore my preferred workflow is to “think bigger”. Imagine being a product owner of a huge, sluggish company. The left hand never knows what the right hand does or has done (i.e. the LLM forgetting things already exist in the codebase), everything has to be rigorous defined, specified and enforced. Some people reported good results with Test-Driven-Development (TDD) - but in my experience these things only prevent regressions and not proactively enforce the desired agent behaviour.
@@ -47,9 +47,9 @@ This could be a bug, a feature, some changes to existing behaviour etc.
This dives into the codebase to asses the current state of things. Maybe some bugs are obvious and easily fixed.
This formalizes that the LLM understood what should be done and especially what is out of scope.
Pin the desired behaviour in a Specification.
-Either this means changing currently established specifications (i.e. bug/chang) or writing complete new ones (i.e. feature).
+Either this means changing currently established specifications (i.e. bug/change) or writing complete new ones (i.e. feature).
Investigate Spec-Compliance.
-Again the agentlooks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.
+Again the agent looks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.
Generate Tasks.
From the compliance-report of spec-deviations (either from a bug or from a new/changed spec) finally a Plan to fix everything can be derived (think: Sprint-Planning).
NOTE: Up to here the agent never touched the code.
@@ -206,15 +206,26 @@ font-style: inherit;">### Purpose
background-color: null;
font-style: inherit;">### Output
-• Create /tasks/- Create /tasks/<feature>/PRD.md • Markdown only – no prose, no code‑fences. •
-File structure: # >/PRD.md
+- Markdown only – no prose, no code‑fences.
+- File structure:
+
+ > # <Feature title> ## 1. Problem / Motivation ## 2. Goals ## 3.
-Non‑Goals ## 4. Target Users & Personas ## 5. User Stories (Gherkin
-“Given/When/Then”) ## 6. Acceptance Criteria ## 7. Technical Notes /
-Dependencies ## 8. Open Questions
-
->
+ >
+ > ## 1. Problem / Motivation
+ >
+ > ## 2. Goals
+ >
+ > ## 3. Non‑Goals
+ >
+ > ## 4. Target Users & Personas
+ >
+ > ## 5. User Stories (Gherkin “Given/When/Then”)
+ >
+ > ## 6. Acceptance Criteria
+ >
+ > ## 7. Technical Notes / Dependencies
+ >
+ > ## 8. Open Questions
+
+### Process
-
-
+1. Stakeholder provides a single‑sentence feature idea and invokes this rule.
-2. Look at specifications in `specs/` and inspect the code if needed to get an
- idea what the Stakeholder expects from this feature.
- idea what the Stakeholder expects from this feature.
+3. Ask up to five clarifying questions (Q1 … Q5). If anything is still vague
- after five, look into the project with the new information provided. You may
- ask for further clarification up to 3 times following this schema, else flag
- it in _Open Questions_.
- after five, look into the project with the new information provided. You may
+ ask for further clarification up to 3 times following this schema, else flag
+ it in _Open Questions_.
+4. After questions are answered reply exactly: Ready to generate the PRD.
-5. On a user message that contains only the word "go" (case‑insensitive): •
- Generate /tasks/5. On a user message that contains only the word "go" (case‑insensitive):
+ - Generate /tasks/<feature>/PRD.md following _Output_ spec. • Reply:
->/PRD.md following _Output_ spec.
+ - Reply: <feature>/PRD.md created – review it.
-
+6. STOP. Do **not** generate tasks or code in this rule.
-
-
+### Writing guidelines
-
-• Keep each bullet ≤120 characters. • Use action verbs and measurable language.
-• Leave TBDs only in _Open Questions_. • No business fluff – pretend the reader
-is a junior developer.
-
-
+- Keep each bullet ≤120 characters.
+- Use action verbs and measurable language.
+- Leave TBDs only in _Open Questions_.
+- No business fluff – pretend the reader is a junior developer.
+
+### Safety rails
-
-• Assume all work happens in a non‑production environment, unless otherwise
-stated or requested by you. • Do not include sensitive data or credentials in
-the PRD. • Check the generated Document with
+`markdownlint` (if available),
-apply auto-fixes and fix the remaining issues manually.
+font-style: inherit;">- Assume all work happens in a non‑production environment, unless otherwise
+ stated or requested by you.
+- Do not include sensitive data or credentials in the PRD.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
A call to this rule usually looks like @generate-prd We noticed, that …. Therefore investigate the codebase to come up with a PRD addressing these issues..
@@ -368,296 +445,293 @@ font-style: inherit;">```
2. **Scope and Boundaries**
-
- - What is included/excluded
- - Dependencies on other specifications
- - Relationship to other components
-
-
+3. **Detailed Requirements**
-
- - Structured by logical sections
- - Clear, unambiguous language
- - Examples where helpful
-
-
+4. **Error Handling**
-
- - How errors should be handled
- - Fallback behaviors
- - Edge cases
-
-
+5. **Testing Requirements**
- - Expected test coverage
- - Snapshot requirements
- - Approval test criteria
-
-
+## Writing Standards
-
-
+### Clarity and Precision
-
-
+- **Use specific language**: Avoid vague terms like "should" or "might"
-- **Provide examples**: Include concrete examples for complex requirements
-- **Define terms**: Clearly define any technical terms or concepts
-- **Use consistent formatting**: Follow established patterns from existing specs
-
-
+### Structure and Organization
-
-
+- **Logical flow**: Organize sections in logical order
-- **Consistent headings**: Use consistent heading levels and naming
-- **Cross-references**: Link to related specifications using
-`[spec_name](mdc:specs/spec_name.md)`
-- **Code blocks**: Use appropriate language tags for code examples
-
-
+### Completeness
-
-
+- **Cover all cases**: Address normal, error, and edge cases
-- **Be exhaustive**: Don't assume implementation details
-- **Consider interactions**: How this spec relates to others
-- **Future-proof**: Consider potential changes and extensions
-
-
+## Specification Maintenance
-
-
+### Version Control
-
-
+- **Update specs first**: When changing behavior, update spec before
- implementation
- implementation
+- **Document changes**: Use clear commit messages explaining spec changes
-- **Review process**: Have specs reviewed before implementation
-
-
+### Consistency Checks
-
-
+- **Cross-reference validation**: Ensure all links to other specs are valid
-- **Terminology consistency**: Use consistent terms across all specs
-- **Format consistency**: Follow established formatting patterns
-
-
+### Testing Integration
-
-
+- **Spec-driven tests**: Write tests based on specification requirements
-- **Snapshot validation**: Ensure snapshots match specification exactly
-- **Approval tests**: Use approval tests to catch spec violations
-
-
+## Quality Checklist
-
-
+### Before Finalizing Specification
-
-
+- [ ] All requirements clearly stated
-- [ ] Examples provided for complex requirements
-- [ ] Error cases covered
-- [ ] Cross-references to other specs included
-- [ ] Out of scope items clearly defined
-- [ ] Testing requirements specified
-- [ ] Consistent formatting throughout
-- [ ] Check the generated Document with `markdownlint` (if available), apply
- auto-fixes and fix the remaining issues manually.
-
- auto-fixes and fix the remaining issues manually.
+
+### Review Criteria
-
-
+- [ ] Is the specification unambiguous?
-- [ ] Are all edge cases covered?
-- [ ] Does it integrate well with other specs?
-- [ ] Is it testable?
-- [ ] Is it maintainable?
-
-
+## Common Pitfalls to Avoid
-
-
+### Ambiguity
-
-
+- **Vague language**: "The system should handle errors gracefully"
-- **Missing details**: Not specifying exact error handling behavior
-- **Unclear relationships**: Not explaining how components interact
-
-
+### Inconsistency
-
-
+- **Different terms**: Using different terms for the same concept
-- **Inconsistent formatting**: Not following established patterns
-- **Conflicting requirements**: Requirements that contradict other specs
-
-
+### Incompleteness
-
-
+- **Missing edge cases**: Not considering unusual scenarios
-- **Incomplete examples**: Examples that don't cover all cases
-- **Missing error handling**: Not specifying what happens when things go wrong
-
-
+## Related Rules
-
-
+- spec-compliance-investigation.mdc](mdc:.cursor/rules/spec-compliance-investigation.mdc)
- How to investigate spec-implementation discrepancies
- How to investigate spec-implementation discrepancies
+- base_overview.mdc](mdc:.cursor/rules/base_overview.mdc) Project structure and
- conventions
+ conventions
As it is obvious this is a very intricate rule with many criteria. For this you really need a reasoning and deep-thinking model that can also reason for extended times (many minutes are normal!) and call tools every now and then to get even more information. Models like o3, deepseek-r1 and the opus-series of claude really shine here.
@@ -1054,24 +1128,37 @@ font-style: inherit;">### Purpose
background-color: null;
font-style: inherit;">### Output
-• Create /tasks/- Create /tasks/<feature>/TASKS.md (overwrite if it exists). • Markdown only, no
-prose around it. • Epics = H2 headings (>/TASKS.md (overwrite if it exists).
+`## 1. <Epic>`). • Tasks = unchecked
-check‑boxes (- Markdown only, no prose around it.
+`- [ ] 1.1 <task>`). • Sub‑tasks = indent one space under their
-parent (- Epics = H2 headings (` - [ ] 1.1.1 <subtask>`). • Create a
-/tasks/`## 1. <Epic>`).
+- Tasks = unchecked check‑boxes (`- [ ] 1.1 <task>`).
+- Sub‑tasks = indent one space under their parent (` - [ ] 1.1.1 <subtask>`).
+- Create a /tasks/<subtask>.md (i.e. >.md (i.e.
+`Task_3_2_4.md` for Epic
-3, Task 2, Subtask 4)
-
-`Task_3_2_4.md` for Epic 3, Task 2, Subtask 4)
+
+### Process
-
-1. Read the tagged PRD.
+
1. Read the tagged PRD.
+2. **Investigate** the current state of the repository to collect answers to
- your first questions. All specs for fixed behaviours and outputs are located
- in your first questions. All specs for fixed behaviours and outputs are located
+ in `specs/`. **Consult those** as a source first before trying to
- reverse-engineer from the code.
- If specs are in need of change then this is also a task to be generated.
- reverse-engineer from the code.
+ If specs are in need of change then this is also a task to be generated.
+3. If critical info is missing and cannot be answered by looking at the code,
- ask max five clarifying questions (Q1 … Q5) and stop until answered.
- ask max five clarifying questions (Q1 … Q5) and stop until answered.
+4. After questions are answered think about the answers and: Either: look at the
- code again, then goto 3., and ask for further clarification Or: Reply
- exactly: Ready to generate the subtasks – respond **go** to proceed.
- code again, then goto 3., and ask for further clarification Or: Reply
+ exactly: Ready to generate the subtasks – respond **go** to proceed.
+5. On a user message that contains only the word "go" (case‑insensitive): a.
- Generate /tasks/ Generate /tasks/<feature>/TASKS.md following _Output_ spec. b. Reply with:
- TASKS.md created – review them.
- TASKS.md created – review them.
+6. After TASKS.md was reviewed, create `Task_<e>_<t>_<s>.md` for each task and
- subtask containing implementation hints like relevant specs (link them!),
- primary files to edit/review for this task, tests needing change, etc.
- subtask containing implementation hints like relevant specs (link them!),
+ primary files to edit/review for this task, tests needing change, etc.
+7. Stop. Do **not** begin executing tasks in this rule.
-
-
+### Writing guidelines
-
-• Each item ≤120 characters, start with an action verb. • Hints are allowed
-below each item as HTML-Comment and do not count against the 120 characters. •
-Group related work into logical epics with ≤7 direct child items. • Prefer
-concrete file paths, commands, specs or APIs when available. • Skip
-implementation details obvious from the codebase in the overview. • If a task
-only concerns up to 5 files, name them in the detailed file. Otherwise give
-hints on how to search for them (i.e. "everything under src/models/").
-
-
+- Each item ≤120 characters, start with an action verb.
+- Hints are allowed below each item as HTML-Comment and do not count against the
+ 120 characters.
+- Group related work into logical epics with ≤7 direct child items.
+- Prefer concrete file paths, commands, specs or APIs when available.
+- Skip implementation details obvious from the codebase in the overview.
+- If a task only concerns up to 5 files, name them in the detailed file.
+ Otherwise give hints on how to search for them (i.e. "everything under
+`src/models/`").
+
+### Safety rails
-
-• Never touch production data. • Assume all work happens in a feature branch,
-never commit directly to main. • Check the generated Document with
-
+`markdownlint` (if available), apply auto-fixes and fix the remaining issues
-manually.
+font-style: inherit;">- Never touch production data.
+- Assume all work happens in a feature branch, never commit directly to main.
+- Check the generated Document with `markdownlint` (if available), apply
+ auto-fixes and fix the remaining issues manually.
This also works better with one of those deep-thinking models.
The codebase we look at here is a project called gitlab_overviewer. It takes gitlab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in gitlab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.
+
The codebase we look at here is a project called gitlab_overviewer. It takes GitLab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in GitLab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.
The current issue is, that due to a complete rewrite we are happy with the current output, but there are some minor problems still remaining. Some of them may also due to specs being incorrect, because requirements or ideas changed over time.
Project Integrity: Through rigorous spec adherence and systematic task breakdown, the codebase is more consistent, much better documented and decently tested5.
Reduced Cognitive Load: The Cursor rule system has made it easier to focus on actual development tasks instead of wrestling with the AI’s sometimes questionable assumptions - or just installing a third different testing framework for exactly this module.
-
5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.
In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the forseeable future, so if you are looking for a project, feel free!
+
5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.
In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the foreseeable future, so if you are looking for a project, feel free!
All in all this isn’t a silver bullet for all AI-assisted development problems, but it’s made my coding experience with Cursor much more productive and predictable. It turns out treating an AI as a slightly overeager junior developer who needs clear instructions works better than hoping it’ll just “do the right thing”.
Weitere Instanzen und Definitionen, die der Generator (noch) nicht macht
@@ -6681,7 +6789,6 @@ background-color: null;
font-style: inherit;">Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).
-{{< dend >}}
Was noch zu tun ist
@@ -6974,50 +7081,6 @@ font-style: inherit;">--interleaved-output
https://drezil.de/Health/Issues.htmlMon, 31 Dec 2018 23:00:00 GMT
-
- Fortgeschrittene funktionale Programmierung in Haskell
- https://drezil.de/Coding/Haskell/FFPiH.html
- FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.
-
Insgesamt haben wir die Vorlesung 3x gehalten, wobei von der ersten zur zweiten Iteration der Inhalt massiv überarbeitet wurde und bei der Iteration von der zweiten zur dritten Vorlesung die Übungen komplett neu erstellt wurden.
Einige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.
-
-
Aufbau der Vorlesung
-
Vorausgesetzt wurde, dass die Studierenden das erste Semester abgeschlossen hatten und somit bereits leichte Grundlagen in Haskell kannten (aber z.b. Dinge wie Functor/Applicative/Monad noch nicht wirklich erklärt bekommen haben).
-
Stück für Stück werden die Studis dann zunächst in abstrakte Konstrukte eingeführt, aber diese werden dann schnell in die Praxis umgesetzt. Etwa mit dem Schreiben eines eigenen Parsers.
-
Schlussendlich gibt es dann einen “Rundumschlag” durch die gesamte Informatik. Erstellung eines Spieles (auf basis einer kleinen Grundlage), erstellung von WebApps mit Yesod, Parallelisierung und Nebenläufigkeit für rechenintensive Anwendungen inkl. synchronisation mittels STM.
-
Optional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.
-
Ziel hierbei war nicht, diese ganzen Themen in der Tiefe beizubringen, sondern aufzuzeigen, wie sie sehr schnell abstrakte Konstrukte, die ihnen ggf. 3 Semester später erst begegnen bugfrei benutzen können, da Haskell hier in sehr vielen Fällen einfach nur die “richtige” Lösung kompilieren lässt und alle gängigen Fallen schlicht ausschließt. Beispiel ist z.b. STM innerhalb von STM, Mischen von DB-Monade, Handler-Monade und Template-Engine in Yesod, Process () statt IO () in der Nutzung von CloudHaskell, etc. pp.
-
-
-
Studentisches Feedback
-
Sehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:
-
Übung 2, Aufgabe 2, weil hier durch “einfaches” umformen hin zu Abstraktionen und mit den Regeln dieser im ersten Fall die Laufzeit (vor Compileroptimierungen) von O(n²) auf O(0) ändert.
-
Übung 4, welche ein komplett fertigen (sehr rudimentären und simplen) Dungeon-Crawler bereitstellt, der “nur” 1-2 bugs hat und “wie ein echtes Projekt” erweitert werden muss. Diese Übung hat sich dann über 4 weitere Übungen gestreckt, wobei folgende Aufgaben gelöst werden müssen:
-
-
Einarbeitung in QuickCheck zur Behebung eines Bugs im Test
-
Umschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful Lenses
-
Continuation-Basierendes Event-System
-
Hinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser
-
Ändern des GUI-Parts von CLI auf 2D GL mittels gloss
-
Ändern von StateT World auf RWST GameConfig Log World und somit nutzen von individuellen Konfigurationen für z.b. Keybindings
-
-
-
-
-
- ]]>
- Lecture
- Haskell
- https://drezil.de/Coding/Haskell/FFPiH.html
- Sun, 31 Dec 2017 23:00:00 GMT
-
Lenses
https://drezil.de/Coding/Haskell/Lenses.html
@@ -8840,6 +8903,50 @@ font-style: inherit;">-> f t
https://drezil.de/Coding/Haskell/Lenses.htmlSun, 31 Dec 2017 23:00:00 GMT
+
+ Fortgeschrittene funktionale Programmierung in Haskell
+ https://drezil.de/Coding/Haskell/FFPiH.html
+ FFPiH ist eine Vorlesung, die ich zusammen mit einem Kommilitonen im Sommer 2015 erstmals erstellt und gehalten haben.
+
Insgesamt haben wir die Vorlesung 3x gehalten, wobei von der ersten zur zweiten Iteration der Inhalt massiv überarbeitet wurde und bei der Iteration von der zweiten zur dritten Vorlesung die Übungen komplett neu erstellt wurden.
Einige der aktualisierten Übungen sind privat geschaltet, da diese iterativ aufeinander aufbauen und jeweils die Musterlösung der vorherigen enthalten.
+
+
Aufbau der Vorlesung
+
Vorausgesetzt wurde, dass die Studierenden das erste Semester abgeschlossen hatten und somit bereits leichte Grundlagen in Haskell kannten (aber z.b. Dinge wie Functor/Applicative/Monad noch nicht wirklich erklärt bekommen haben).
+
Stück für Stück werden die Studis dann zunächst in abstrakte Konstrukte eingeführt, aber diese werden dann schnell in die Praxis umgesetzt. Etwa mit dem Schreiben eines eigenen Parsers.
+
Schlussendlich gibt es dann einen “Rundumschlag” durch die gesamte Informatik. Erstellung eines Spieles (auf basis einer kleinen Grundlage), erstellung von WebApps mit Yesod, Parallelisierung und Nebenläufigkeit für rechenintensive Anwendungen inkl. synchronisation mittels STM.
+
Optional gab es weitere Übungen zu dingen wie “verteiltes Rechnen”.
+
Ziel hierbei war nicht, diese ganzen Themen in der Tiefe beizubringen, sondern aufzuzeigen, wie sie sehr schnell abstrakte Konstrukte, die ihnen ggf. 3 Semester später erst begegnen bugfrei benutzen können, da Haskell hier in sehr vielen Fällen einfach nur die “richtige” Lösung kompilieren lässt und alle gängigen Fallen schlicht ausschließt. Beispiel ist z.b. STM innerhalb von STM, Mischen von DB-Monade, Handler-Monade und Template-Engine in Yesod, Process () statt IO () in der Nutzung von CloudHaskell, etc. pp.
+
+
+
Studentisches Feedback
+
Sehr gutes Feedback von den Studenten bekamen wir insbesondere für Übungen wie:
+
Übung 2, Aufgabe 2, weil hier durch “einfaches” umformen hin zu Abstraktionen und mit den Regeln dieser im ersten Fall die Laufzeit (vor Compileroptimierungen) von O(n²) auf O(0) ändert.
+
Übung 4, welche ein komplett fertigen (sehr rudimentären und simplen) Dungeon-Crawler bereitstellt, der “nur” 1-2 bugs hat und “wie ein echtes Projekt” erweitert werden muss. Diese Übung hat sich dann über 4 weitere Übungen gestreckt, wobei folgende Aufgaben gelöst werden müssen:
+
+
Einarbeitung in QuickCheck zur Behebung eines Bugs im Test
+
Umschreiben von explizitem Argument-Passing hin zu Monad-Transformers mit stateful Lenses
+
Continuation-Basierendes Event-System
+
Hinzufügen eines Parsers für Level, Items & deren Effekte und implementation dieser
+
Ändern des GUI-Parts von CLI auf 2D GL mittels gloss
+
Ändern von StateT World auf RWST GameConfig Log World und somit nutzen von individuellen Konfigurationen für z.b. Keybindings
+
+
+
+
+
+ ]]>
+ Lecture
+ Haskell
+ https://drezil.de/Coding/Haskell/FFPiH.html
+ Sun, 31 Dec 2017 23:00:00 GMT
+Monoid? Da war doch was…
https://drezil.de/Coding/Haskell/Code Snippets/Monoid.html
diff --git a/dist/listings.json b/dist/listings.json
index e629cfe..c8b26d9 100644
--- a/dist/listings.json
+++ b/dist/listings.json
@@ -11,8 +11,8 @@
"/Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
"/Coding/Haskell/Webapp-Example/index.html",
"/Health/Issues.html",
- "/Coding/Haskell/FFPiH.html",
"/Coding/Haskell/Lenses.html",
+ "/Coding/Haskell/FFPiH.html",
"/Coding/Haskell/Code Snippets/Monoid.html",
"/Coding/Haskell/Code Snippets/Morphisms.html",
"/Coding/Haskell/Advantages.html",
diff --git a/dist/search.json b/dist/search.json
index c3c7c3d..950a2e8 100644
--- a/dist/search.json
+++ b/dist/search.json
@@ -144,146 +144,81 @@
]
},
{
- "objectID": "Writing/documentation.html",
- "href": "Writing/documentation.html",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "objectID": "Writing/coding-age-ai.html",
+ "href": "Writing/coding-age-ai.html",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
"section": "",
- "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering [1, 2] und damit einhergehender Prinzipien wie die des Endings-Projekts für digitale Langlebigkeit [3] und FAIR4RS-Prinzipien[4].\nZiel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen[5].\nIm Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
+ "text": "In the fast evolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize niche problems that can benchmarked[2]1.\n1 Like all decent humans i ain’t got time to read up on everything - so a big shoot-out to [3] for doing thorough reviews on ML-topics and linking the respective papers!2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verifiedI personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.\nTherefore see this “Field Guide” for what it is: A current state of things that work for at least 1 Individuum in exactly this ecosystem at this point in time.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
- "objectID": "Writing/documentation.html#einleitung",
- "href": "Writing/documentation.html#einleitung",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "objectID": "Writing/coding-age-ai.html#introduction",
+ "href": "Writing/coding-age-ai.html#introduction",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
"section": "",
- "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering [1, 2] und damit einhergehender Prinzipien wie die des Endings-Projekts für digitale Langlebigkeit [3] und FAIR4RS-Prinzipien[4].\nZiel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen[5].\nIm Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
+ "text": "In the fast evolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize niche problems that can benchmarked[2]1.\n1 Like all decent humans i ain’t got time to read up on everything - so a big shoot-out to [3] for doing thorough reviews on ML-topics and linking the respective papers!2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verifiedI personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.\nTherefore see this “Field Guide” for what it is: A current state of things that work for at least 1 Individuum in exactly this ecosystem at this point in time.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
- "objectID": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
- "href": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Inhaltliche Anforderungen an die Dokumentation",
- "text": "Inhaltliche Anforderungen an die Dokumentation\nEin zentrales Problem in der Dokumentation wissenschaftlicher Software ist oft das fehlende Big Picture, also eine klare Darstellung des Was und Warum. Die Dokumentation sollte daher alle Informationen abdecken, die zum Verstehen, Nutzen und Weiterentwickeln der Software nötig sind[3, 6]. Insbesondere sind folgende Inhalte essenziell:\n\nZiel und Zweck der Software (Statement of Need)\nBeschreiben Sie was die Software tut und warum sie entwickelt wurde. Nennen Sie den wissenschaftlichen Zweck, das Forschungsproblem oder die Fragestellung, die mit der Software adressiert wird, sowie die Zielgruppe (wer soll sie nutzen?). Dieser Kontext hilft anderen, den Nutzen der Software einzuschätzen. Eine klare Problem- und Zielbeschreibung richtet sich auch nach dem Umfeld ähnlicher Lösungen – falls es bereits etablierte Tools gibt, sollte die Dokumentation die eigene Herangehensweise einordnen (z. B. was die Software anders oder besser macht).Beispiel: “Dieses Tool extrahiert Personen-Netzwerke aus historischen Briefkorpora, um sozialwissenschaftliche Analysen zu ermöglichen.”\n\n\nInput-/Output-Spezifikation und Datenbeschreibung\nDokumentieren Sie alle Eingabeformate, Ausgabedaten und verwendeten Datensätze. Nutzer*innen müssen wissen, welche Daten die Software erwartet (Dateiformate, Schnittstellen, Parameter) und welche Ergebnisse sie produziert. Idealerweise werden Beispiele angegeben: z. B. Beispiel-Dateien oder -Parameter und die korrespondierende Ausgabe.\nFalls die Software mit bestimmten Forschungsdaten arbeitet, beschreiben Sie diese Daten und ihre Struktur. Dies umfasst die Datenmodelle (etwa wichtige Felder, deren Bedeutung und kontrollierte Vokabulare) und Annahmen über die Daten.\nGemäß den ENDINGS-Prinzipien sollte die Datenstruktur auch in einem statischen Dokument festgehalten und der Software beigelegt sein – so bleibt nachvollziehbar, wie die Software die Daten interpretiert [3]. Eine Tabelle oder Auflistung der Eingabefelder und Ausgabegrößen mit kurzen Beschreibungen erhöht die Klarheit. Beispiel: “Eingabedatei: CSV mit Spalten Autor, Empfänger, …; Ausgabe: JSON-Datei mit Netzwerk-Metriken pro Briefwechsel.”\nGerade für JSON-Dateien bietet es sich an ggf. auch ein formelle Spezifikation via JSON-Schema an.\n\n\nCode-Abhängigkeiten und technische Voraussetzungen\nListen Sie alle Abhängigkeiten (Dependencies) der Software auf. Dazu gehören verwendete Programmiersprachen/Versionen, erforderliche Bibliotheken oder Frameworks, und sonstige Systemvoraussetzungen (z. B. Betriebssystem, Mindesthardware, Datenbank-Versionen). Wichtig ist, wie diese Abhängigkeiten installiert werden können. Optimal ist eine automatisierte Installationsroutine (z. B. ein requirements.txt für Python oder ein Paketmanager-Befehl). In jedem Fall sollte die Dokumentation mindestens Schritt-für-Schritt-Installationsanleitungen enthalten (inklusive evtl. benötigter Vorkenntnisse, z. B. “Python 3 erforderlich”). Beispiel: “Benötigt Python 3.9 und die Bibliotheken Pandas und NetworkX. Installation: pip install -r requirements.txt.” Falls spezielle technische Voraussetzungen bestehen – etwa Zugriff auf bestimmte Hardware, ein Hochleistungsrechner oder große Speicherkapazitäten – sind diese zu nennen.\n\n\nWissenschaftlicher Hintergrund und theoretischer Kontext\nDa es sich um Forschungssoftware handelt, sollten Sie den wissenschaftlichen Kontext 1 offenlegen. Das heißt, erklären Sie die grundlegenden Methoden, Algorithmen oder Modelle, die in der Software umgesetzt sind, zumindest in Überblicksform.\n1 Dieser Hintergrundteil unterscheidet Forschungssoftware-Dokumentation von rein kommerzieller Dokumentation: Es geht nicht nur um wie man das Tool benutzt, sondern auch warum es so funktioniert (Stichwort Nachvollziehbarkeit).Verweisen Sie auf relevante Publikationen oder Theorien, damit andere die wissenschaftliche Grundlage nachvollziehen können. Halten Sie diesen Abschnitt aber prägnant – Details gehören in die Forschungsarbeit selbst.Beispielsweise: “Die Implementierung folgt dem Algorithmus von Müller et al. (2019) zur Netzwerkanalyse historischer Korrespondenz.”\nWichtig ist, dass die Dokumentation den Brückenschlag zwischen Code und Forschung herstellt. Da viele Wissenschaftler*innen zentrale Aspekte lieber in ihren Artikeln dokumentieren, sollte in der Software-Dokumentation zumindest eine Zusammenfassung mit Querverweis erfolgen. So wissen Nutzer*innen, unter welchen Annahmen oder Theorien das Tool funktioniert.\n\n\nBekannte Limitationen, Annahmen und Fehlermeldungen\nGeben Sie ehrlich Auskunft über die Grenzen der Software:\n\nWelche Fälle werden nicht abgedeckt?\nWelche Annahmen über die Daten oder Anwendungsszenarien werden getroffen?\n\n Solche Hinweise verhindern Fehlanwendungen und sparen Nutzern Zeit.Beispielsweise: “funktioniert nur für Deutschsprachige Texte” oder “maximale Datenmenge 1 Mio. Datensätze, da Speicherbegrenzung”\nFalls es bekannte Bugs oder Workarounds gibt, sollten diese ebenfalls (etwa in einer FAQ oder einem Abschnitt “Bekannte Probleme”) erwähnt werden.\nAuch aussagekräftige Fehlermeldungen im Programm selbst sind eine Form von Dokumentation: Sie sollten nicht nur kryptisch abbrechen, sondern dem/der Anwender*in idealerweise mitteilen, was schiefging und bestenfalls direkt wie es behoben werden kann. Solche in den Code integrierten Hinweise ergänzen die schriftliche Dokumentation und tragen zur besseren Nutzbarkeit bei.Beispiel: “Fehler: Ungültiges Datum im Feld XY – bitte Format TT/MM/JJJJ verwenden.”\n\n\nWeiterentwicklung und Beitragsmöglichkeiten\nObwohl viele Digital-Humanities-Tools primär von Einzelpersonen genutzt werden, sollte dennoch angegeben werden, wie andere ggf. zur Software beitragen oder Support erhalten können. Ein kurzer Hinweis auf den Issue-Tracker (z. B. “Fehler bitte über GitHub-Issues melden”) oder auf die Kontaktmöglichkeit zum*zur Autor*in (E-Mail) gehört dazu.\nEbenso können Community Guidelines skizziert werden: etwa Code-Standards oder ein Verhaltenskodex, falls Beiträge erwartet werden. Für kleinere Projekte reicht oft ein Satz wie “Beiträge durch Pull Requests sind willkommen; bei Fragen wenden Sie sich an…”. 2\n2 Dieser Aspekt muss nicht umfangreich sein, zeigt aber Offenheit und sorgt dafür, dass im Falle von Rückfragen die Hürde für Kontaktaufnahme niedrig ist.\n\nProjekt-Metadaten (Lizenz, Zitation, Version)\nTeil der Dokumentation sind auch formale Informationen, die im Repository leicht zugänglich sein sollten[6]. Lizenzinformationen klären die rechtlichen Bedingungen der Nutzung und Weiterverbreitung. Es ist Best Practice, eine LICENSE-Datei beizulegen, aber auch in der README kurz zu erwähnen, unter welcher Lizenz die Software steht. Für Forschungssoftware empfiehlt sich eine offene Lizenz (z. B. MIT, BSD oder Apache 2.0 für Code, CC-BY für Daten), um Nachnutzung nicht zu behindern.\nZudem sollte angegeben werden, wie die Software zitiert werden kann (z. B. DOI, Paper-Referenz). Ein eigener Abschnitt “Zitation” oder eine CITATION-Datei beschreibt, welche Publikation oder welcher DOI bei Verwendung der Software in wissenschaftlichen Arbeiten anzugeben ist. Dies erhöht die akademische Sichtbarkeit und stellt sicher, dass Autor*innen Credits für ihre Software bekommen [7].\nSchließlich ist es sinnvoll, eine Versionsnummer der Software zu nennen (idealerweise in README und im Tool selbst), damit Nutzer wissen, auf welche Ausgabe sich die Dokumentation bezieht – insbesondere, wenn es im Laufe der Zeit Aktualisierungen gibt[3].\n\n\nZusammenfassung der inhaltlichen Anforderungen\n\n\n\n\n\n\nZusammengefasst sollte die Dokumentation alle W-Fragen beantworten\n\n\n\n\nWas tut die Software,\nwarum wurde sie geschrieben (wissenschaftlicher Zweck),\nwer soll sie nutzen,\nwie wird sie benutzt (Inputs, Outputs, Abläufe),\nwomit läuft sie (Umgebung/Abhängigkeiten),\nunter welchen Bedingungen (Annahmen/Limitationen) und\nwohin können sich Nutzer wenden (Support/Zitation).\n\n\n\nAll diese Punkte sorgen für Nachvollziehbarkeit (im Sinne von Reproduzierbarkeit der Ergebnisse) und Weiterverwendbarkeit (im Sinne von Adaptierbarkeit der Software für neue Kontexte).",
+ "objectID": "Writing/coding-age-ai.html#how-to-program-with-cursor",
+ "href": "Writing/coding-age-ai.html#how-to-program-with-cursor",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
+ "section": "How to program with Cursor",
+ "text": "How to program with Cursor\nIn essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.\n\nCapabilities / General procedure\nThe main issue is now: theoretically agentic IDEs can get all information - practically it is limited directly by token-window sizes, costs of these queries; and indirectly by outsourced costs like environmental impacts, data-security, etc. The suppliers of such services can claim privacy as much as they want - it can’t be proven and (especially under US-Law) is not even possible to resist lawful orders (including the gag-order to not talk about these).\nIn practise one feels the direct pain points more severely. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.\nTherefore my preferred workflow is to “think bigger”. Imagine being a product owner of a huge, sluggish company. The left hand never knows what the right hand does or has done (i.e. the LLM forgetting things already exist in the codebase), everything has to be rigorous defined, specified and enforced. Some people reported good results with Test-Driven-Development (TDD) - but in my experience these things only prevent regressions and not proactively enforce the desired agent behaviour.\n\n\nLessons from Project Management\nThis may be a duh!-Moment for people longer in Software-Engineering, but most of the time is getting the specifications of what you want to build right. Asking questions. Interviewing stakeholders. Gathering user experience reports. Most of it is not actually writing code - but deciding on what to write and how.\nFor this i created some rules in my workflow that interleave with their responsibilities and outcomes. Especially in the planning phase the LLM is encouraged to ask questions, find edge-cases or even look at other implementations. One could also think of letting the agent do a websearch, read reports and forums on how good which framework works and then arguments on why this framework is good - and also why it is not good. The last decision of all pro/contra is by the actual human.\nThe main theme always follows a similar pattern:\n\nA need is found.\nThis could be a bug, a feature, some changes to existing behaviour etc.\nAn investigation is launched, yielding a Product Requirement Document (PRD).\nThis dives into the codebase to asses the current state of things. Maybe some bugs are obvious and easily fixed.\nThis formalizes that the LLM understood what should be done and especially what is out of scope.\nPin the desired behaviour in a Specification.\nEither this means changing currently established specifications (i.e. bug/change) or writing complete new ones (i.e. feature).\nInvestigate Spec-Compliance.\nAgain the agent looks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.\nGenerate Tasks.\nFrom the compliance-report of spec-deviations (either from a bug or from a new/changed spec) finally a Plan to fix everything can be derived (think: Sprint-Planning).\nNOTE: Up to here the agent never touched the code.\nFinally Implement the change.\nThis is most often the most trivial step. Everything is known and formulated for “simple” Agents to just follow. It names files needed, specs to follow, guidelines on how to do things in the repository etc.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
- "objectID": "Writing/documentation.html#format-und-struktur-der-dokumentation",
- "href": "Writing/documentation.html#format-und-struktur-der-dokumentation",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Format und Struktur der Dokumentation",
- "text": "Format und Struktur der Dokumentation\nFür Forschende ohne viel Ressourcen muss die Dokumentation einfach zugänglich, leicht pflegbar und ohne Spezialsoftware erstellbar sein.\n\nREADME.md als zentrales Dokument\nDie Hauptdokumentation sollte als README in Markdown-Format im Hauptverzeichnis des Code-Repositoriums liegen. Dieses README fungiert als “Startseite” des Projekts und enthält idealerweise eine komprimierte Übersicht aller wichtigen Punkte:\n\nZweck der Software,\nKurzbeschreibung,\nInstallation,\nkurzer Nutzungsbeispiel,\nKontakt/Lizenz.\n\nAuf Plattformen wie GitHub, GitLab etc. wird die README automatisch angezeigt, was die Sichtbarkeit erhöht. Die Vorteile von Markdown sind die einfache Lesbarkeit in Rohform, die breite Unterstützung (auch in Renderern wie GitHub-Webansicht) und die Eignung für Versionierung (Textdatei im git). So bleibt die Dokumentation eng mit dem Code verzahnt und unter Versionskontrolle – denn Dokumentation soll statisch und zusammen mit den Daten/Code abgelegt werden[3].\n\n\nKeine proprietären Formate oder Abhängigkeit von Werkzeugen\n\n\n\n\n\n\n\nPrinzip\n\n\n\nDokumentation gehört zum Code und muss auch ohne Programm lesbar sein.\n\nUm Hürden für die Erstellung und Nutzung der Dokumentation gering zu halten, sollte auf gängige, offene Formate gesetzt werden (Plaintext, Markdown, reStructuredText).\nVermeiden Sie nach Möglichkeit Formate wie Word-Dokumente oder PDF als primäre Dokumentationsquelle – solche Formate sind nicht diff-freundlich, erschweren Zusammenarbeits-Workflows und sind meist nicht Teil des Versionskontrollsystems. Ein Markdown-Dokument hingegen kann gemeinsam mit dem Code gepflegt werden, und Änderungen sind transparent nachvollziehbar.\nIm Sinne der Digital Longevity[3] ist eine statische HTML- oder PDF-Version der Dokumentation (automatisch generiert aus Markdown via pandoc) als Teil der Release-Artefakte sinnvoll. Wichtig ist aber, dass die Quelle der Wahrheit immer die im Repository gepflegte Doku bleibt.\n\n\nStrukturierte Unterteilung in weitere Dateien/Abschnitte\n\n\nexample-project/\n├── README.md\n├── CONTRIBUTING.md (optional)\n├── CHANGELOG.md (optional)\n├── CITATION.md (oder CITATION.cff)\n├── LICENSE\n├── data/ (optional)\n│ └── sample_data.csv\n├── docs/ (optional)\n│ ├── INSTALL.md\n│ └── USAGE.md\n├── examples/ (optional)\n│ └── example_workflow.ipynb\n└── src/\n ├── script.py\n └── module/\n └── helper.py\nBeispielhafter Struktur eines Code-Repositories\nSollte die Dokumentation umfangreicher sein, ist es sinnvoll, sie in logisch getrennte Abschnitte aufzuteilen. Dies kann innerhalb der README durch Überschriften geschehen oder durch zusätzliche Markdown-Dateien im Repository (z. B. eine INSTALL.md für ausführliche Installationshinweise, eine USAGE.md oder TUTORIAL.md für detaillierte Benutzeranleitungen, eine CHANGELOG.md für Changelog etc.). Eine gängige Struktur ist z. B.:\n\nREADME.md – Überblick (Ziel, Installation, kurzes Beispiel, Lizenz/Zitation)\ndocs/ Verzeichnis mit weiteren .md-Dateien für tiefergehende Dokumentation (optional)\nCONTRIBUTING.md – Hinweise für Beiträger (falls relevant)\nLICENSE – Lizenztext\nCITATION.cff oder CITATION.md – wie zu zitieren.\n\nDiese Dateien sollten konsistent formatiert und wie oben benannt sein, damit sie leicht auffindbar und ggf. direkt durch Tools verarbeitbar sind.\n\n\nÜbersichtlichkeit und Navigierbarkeit\n\n\n\n\n\n\n\nPrinzip\n\n\n\n“Don’t Repeat Yourself”: Alle Informationen zu einem Thema (Installation/Nutzung/…) an derselben Stelle sammeln und keinesfalls mehrfach pflegen.\n\nStrukturieren Sie die Dokumentation mit klaren Überschriften und Listen, damit Leser schnell die gesuchten Informationen finden. Eine logische Gliederung hilft unterschiedlichen Nutzergruppen gezielt das Relevante zu finden. Für längere Dokumente kann ein Inhaltsverzeichnis oder eine Abschnittsübersicht am Anfang nützlich sein. Markdown bietet z. B. automatische TOC-Generierung auf manchen Plattformen.\nAchten Sie darauf, pro Abschnitt nur zusammenhängende Informationen zu behandeln (z. B. alles zu Installation an einem Ort) und Wiederholungen zu vermeiden. Das Mantra “Don’t Repeat Yourself” gilt auch für Dokumentation.\n\n\nBeispiele, Codeblöcke und ggf. Abbildungen einbinden\n\n\n\n\n\n\n\nPrinzip\n\n\n\nZeigen statt nur beschreiben – konkrete Anwendungsfälle in der Doku verankern.\n\nNutzen Sie die Möglichkeiten von Markdown, um die Dokumentation lebendig zu gestalten. Zeigen Sie Code-Beispiele als formatierte Codeblöcke, fügen Sie Links zu weiterführenden Ressourcen ein, oder binden Sie bei Bedarf Abbildungen ein (etwa ein Diagramm der Datenpipeline, ein Screenshot der Benutzeroberfläche, etc.).\nAchten Sie dabei auf Dateigrößen und Formate (Bilder als PNG/JPG, Diagramme wenn möglich als SVG für Langlebigkeit). Falls Diagramme der Architektur oder Workflow-Abbildungen hilfreich sind, können diese mit simplen Mitteln erstellt werden3.\n3 zur Not handgezeichnet und abfotografiert, besser jedoch mit Tools wie mermaid.js Diagrammen in Markdown oder graphvizDiese Visualisierungen sind jedoch nur dann einzusetzen, wenn sie echten Mehrwert bieten und ohne komplexe Build-Prozesse eingebunden werden können. Im Zweifel hat textuelle Beschreibung Vorrang, um nicht vom Prinzip “keep it simple” abzuweichen.\n\n\n\n\n\n\nTypische Nutzungsszenarien und Workflows\n\n\n\n\n\nZeigen Sie anhand von konkreten Beispielen, wie die Software benutzt wird. Ein Quickstart-Beispiel senkt die Einstiegshürde enorm. Dies kann z. B. eine Anleitung sein, wie man mit wenigen Schritten von einer Eingabedatei zum gewünschten Ergebnis kommt.\nBeschreiben Sie typische Workflows in nachvollziehbaren Schritten: Eingabe vorbereiten, Software-Befehl/GUI-Aktion ausführen, Ausgabe interpretieren. Ggf. können mehrere Anwendungsfälle skizziert werden (z. B. “Analyse eines einzelnen Briefes” vs. “Batch-Verarbeitung eines gesamten Korpus”).\nDiese Beispiele sollten realistisch und möglichst repräsentativ für wissenschaftliche Anwendungen sein. Nutzen Sie gerne kleine, mitgelieferte Datensamples oder Defaults, damit Nutzer die Beispielschritte direkt ausprobieren können. Idealerweise werden Code-Beispiele mit ausgegebenen Resultaten gezeigt (z. B. in Form von Ausschnitten oder, bei Kommandozeilentools, via --help dokumentiert).\n\n\n\n\n\nUmfang und Fokus der Dokumentation\n\n\n\n\n\n\n\nPrinzip\n\n\n\nKann eine neue Person in < 1 Stunde mit Hilfe der Doku das Tool zum Laufen bringen und ein einfaches Beispiel ausführen?\n\nWenn ja, ist der Detailgrad angemessen\nWenn die Person hingegen nach 10 Seiten oder mehr als 1 Stunde immer noch nicht weiß, wie sie loslegen soll, muss die Doku fokussierter werden.\n\n\nEin effizienter Umfang lässt sich erreichen, indem sie alles, was für Nachvollziehbarkeit und Wiederverwendung nötig ist dokumentieren, und alles andere skippen.\n\nNegativbeispiele umfassen:\n\njeder interne Programmiertrick wird erläutert – Quellcode-Kommentare richten sich an Entwickler*innen, während die Nutzendendokumentation sich auf Nutzung und Kontext beschränkt\nseitenlange Theorieabhandlungen (verweisen Sie stattdessen auf Papers)\ngenerische Erklärungen bekannter Technologien (man muss Git oder Python nicht in der Doku erklären, sondern kann referenzieren)\n\n\nHalten Sie auch die Sprache prägnant:\n\nkurze Absätze\nListen\nund einfache Sätze\n\nerhöhen die Lesbarkeit.\nFachtermini aus dem jeweiligen wissenschaftlichen Bereich dürfen verwendet werden, aber erklären/verlinken Sie sie, falls die Zielnutzer sie evtl. nicht kennen.\n\nFokus auf Nutzer*innen - nicht Entwickler*innen\nStellen Sie sich beim Schreiben der Doku die verschiedenen Nutzerrollen vor: “Zukünftiges Ich”, Kolleg*innen, Fachforscher*innen anderer Disziplin und ggf. Software-Entwickler*innen, die den Code erweitern. Jede dieser Gruppen möchte bestimmte Dinge wissen.\nForschende fragen:\n\nWas kann das Tool?\nWie benutze ich es?\nIn welchem Kontext steht es?\n\nEntwicklende Personen fragen:\n\nWie kann ich beitragen?\nWie funktioniert es unter der Haube?\n\nPriorisieren Sie zunächst die erstgenannten (Anwender) – deshalb Fokus auf Zweck, Nutzung und Ergebnisse. Konzentrieren Sie die Hauptdokumentation darauf, das Nutzen und Verstehen der Software von außen zu ermöglichen.\n\n\n\nPriorisierung bei Zeitmangel\nDieser Katalog adressiert primär die Nutzerdokumentation (für Endnutzer und für die Autoren selbst, wenn sie das Tool später wieder anfassen). Entwickler*innendokumentation (z. B. detaillierte API-Dokumente, Code-Kommentare, technische Architektur) kann separat gehalten werden, oder sogar automatisch erzeugt werden.\n\nMinimaldokumentation: kurze Kommentare\nBeginnen Sie mit einer Minimaldokumentation, die alle Schlüsselaspekte abdeckt (“keine Dokumentation” ist keine Option). Good Enough Practices[5] empfehlen, als ersten Schritt zumindest einen kurzen erklärenden Kommentar am Anfang jedes Scripts oder eine README mit ein paar Sätzen zu erstellen. Diese Hürde ist niedrig und bringt bereits Nutzen – selbst wenn (noch) keine ausführliche Handbuch-Doku existiert. Später kann die Dokumentation erweitert werden, insbesondere wenn die Software in Kooperation entsteht oder mehr Nutzer gewinnt. Es hat sich gezeigt, dass ausführliche Dokumentation oft erst entsteht, wenn ein echter Bedarf (z. B. durch externe Nutzer) vorhanden ist. Daher: zögern Sie nicht, zunächst klein anzufangen, aber stellen Sie sicher, dass zumindest die kritischen Informationen sofort verfügbar sind (lieber ein 2-seitiges README heute, als das perfekte 30-seitige Handbuch in zwei Jahren, das evtl. nie geschrieben wird).\n\n\nVerlinkte Dokumentation ist auch Dokumentation\nNutzen Sie Verweise und vorhandene Ressourcen. Wenn z. B. Ihr Tool auf einem komplizierten Setup (Datenbank, Webserver) aufbaut, brauchen Sie nicht jede Installationsoption im Detail in Ihrer Doku zu reproduzieren – verlinken Sie auf offizielle Installationsanleitungen dieser Abhängigkeiten, und nennen Sie nur Ihre spezifischen Konfigurationen und verlinken sie auf die Dokumentation des Setup-Elementes für alles weitere. Ebenso können Tutorials oder Papers, die schon existieren, als weiterführende Links angegeben werden, anstatt Inhalte redundant zu erklären. Das entlastet Ihre Dokumentation und hält sie schlank.\n\n\nUnd anschließend?\nWenn der Zeitmangel vorüber ist4, sollte man nach und nach das folgende Kapitel umsetzen.\n4 als ob DAS je der Fall wäre -.-",
+ "objectID": "Writing/coding-age-ai.html#implementation",
+ "href": "Writing/coding-age-ai.html#implementation",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
+ "section": "Implementation",
+ "text": "Implementation\nI only go briefly over the most important aspects of the implementation in Rules and their results. As this is also evolving with experience, there will be updates to those on the published place.\n\nThe Rules\nCursor rules are written in markdown with a small yaml-frontmatter. description is a string providing the agent with a description of the rule so it can be called like a tool (and basically injects the rule-content into the context). globs automatically inject the rule when the glob matches one attached filename. alwaysApply injects the rule into all contexts-windows.\n\nAlways apply rule: Basic info and behaviour\nThis tells the agent the project-structure and fixes some common pitfalls (i.e. the agent assuming bash, getting sidetracked, etc.).\n\n---\ndescription:\nglobs:\nalwaysApply: true\n---\n\n# IMPORTANT INFORMATION! DO NOT FORGET!\n\n## Conventions\n\n- Run tests with `poetry run pytest`.\n- **NO BASH IDIOMS IN COMMANDS**. Do not assume bash-usage. Use temporary\n scripts instead of bash-idoms like `<<` for text-input to a process.\n- Ask to update corresponding spec when you detect or implement a behaviour\n change.\n- Mention every time things are not implemented according to spec and offer to\n make them compliant.\n\n## Behavior of the Agent\n\n- If you have a task, ONLY do the task. Do not get distracted by failing tests,\n missing data etc. not related to your task.\n- Spec compliance is key. Check corresponding specs before you fix behaviour or\n ask the user if you should proceed.\n\n## Where to look for information?\n\n- Status & changes: `git status`, test failures in `tests/`\n- Active feature work: `/tasks/<feature>/TASKS.md`\n- Feature requirements: `/tasks/<feature>/PRD.md`\n- Feature specifications: `/specs/`\n- Source code: `/src/`\n- Tests & fixtures: `/tests/`\n- CLI entry point: `/src/<project>/cli/__main__.py`\n\n\n\nGet the PRD\n\n---\ndescription:\nglobs:\nalwaysApply: false\n---\n\n## Product Requirements Document (PRD)\n\n### Purpose\n\nDraft a concise, implementation‑ready Product Requirements Document (PRD) from a\none‑sentence feature description plus any additional Q&A with the stakeholder.\n\n### Output\n\n- Create /tasks/<feature>/PRD.md\n- Markdown only – no prose, no code‑fences.\n- File structure:\n\n > # <Feature title>\n >\n > ## 1. Problem / Motivation\n >\n > ## 2. Goals\n >\n > ## 3. Non‑Goals\n >\n > ## 4. Target Users & Personas\n >\n > ## 5. User Stories (Gherkin “Given/When/Then”)\n >\n > ## 6. Acceptance Criteria\n >\n > ## 7. Technical Notes / Dependencies\n >\n > ## 8. Open Questions\n\n### Process\n\n1. Stakeholder provides a single‑sentence feature idea and invokes this rule.\n2. Look at specifications in `specs/` and inspect the code if needed to get an\n idea what the Stakeholder expects from this feature.\n3. Ask up to five clarifying questions (Q1 … Q5). If anything is still vague\n after five, look into the project with the new information provided. You may\n ask for further clarification up to 3 times following this schema, else flag\n it in _Open Questions_.\n4. After questions are answered reply exactly: Ready to generate the PRD.\n5. On a user message that contains only the word \"go\" (case‑insensitive):\n - Generate /tasks/<feature>/PRD.md following _Output_ spec.\n - Reply: <feature>/PRD.md created – review it.\n\n6. STOP. Do **not** generate tasks or code in this rule.\n\n### Writing guidelines\n\n- Keep each bullet ≤120 characters.\n- Use action verbs and measurable language.\n- Leave TBDs only in _Open Questions_.\n- No business fluff – pretend the reader is a junior developer.\n\n### Safety rails\n\n- Assume all work happens in a non‑production environment, unless otherwise\n stated or requested by you.\n- Do not include sensitive data or credentials in the PRD.\n- Check the generated Document with `markdownlint` (if available), apply\n auto-fixes and fix the remaining issues manually.\n\nA call to this rule usually looks like @generate-prd We noticed, that …. Therefore investigate the codebase to come up with a PRD addressing these issues..\n\n\nSpecifications\n\n---\ndescription: Specification Writing Guidelines\nglobs:\nalwaysApply: false\n---\n\n# Specification Writing Guidelines\n\n## Overview\n\nThis rule provides guidelines for writing and maintaining specifications in\n[specs/](mdc:specs) to ensure consistency, clarity, and prevent implementation\ndiscrepancies.\n\n## Specification Structure\n\n### Required Sections\n\nEvery specification should include:\n\n1. **Title and Purpose**\n\n ```markdown\n # Specification: [Component Name]\n\n Brief description of what this specification covers and its purpose.\n ```\n\n2. **Scope and Boundaries**\n - What is included/excluded\n - Dependencies on other specifications\n - Relationship to other components\n\n3. **Detailed Requirements**\n - Structured by logical sections\n - Clear, unambiguous language\n - Examples where helpful\n\n4. **Error Handling**\n - How errors should be handled\n - Fallback behaviors\n - Edge cases\n\n5. **Testing Requirements**\n - Expected test coverage\n - Snapshot requirements\n - Approval test criteria\n\n## Writing Standards\n\n### Clarity and Precision\n\n- **Use specific language**: Avoid vague terms like \"should\" or \"might\"\n- **Provide examples**: Include concrete examples for complex requirements\n- **Define terms**: Clearly define any technical terms or concepts\n- **Use consistent formatting**: Follow established patterns from existing specs\n\n### Structure and Organization\n\n- **Logical flow**: Organize sections in logical order\n- **Consistent headings**: Use consistent heading levels and naming\n- **Cross-references**: Link to related specifications using\n `[spec_name](mdc:specs/spec_name.md)`\n- **Code blocks**: Use appropriate language tags for code examples\n\n### Completeness\n\n- **Cover all cases**: Address normal, error, and edge cases\n- **Be exhaustive**: Don't assume implementation details\n- **Consider interactions**: How this spec relates to others\n- **Future-proof**: Consider potential changes and extensions\n\n## Specification Maintenance\n\n### Version Control\n\n- **Update specs first**: When changing behavior, update spec before\n implementation\n- **Document changes**: Use clear commit messages explaining spec changes\n- **Review process**: Have specs reviewed before implementation\n\n### Consistency Checks\n\n- **Cross-reference validation**: Ensure all links to other specs are valid\n- **Terminology consistency**: Use consistent terms across all specs\n- **Format consistency**: Follow established formatting patterns\n\n### Testing Integration\n\n- **Spec-driven tests**: Write tests based on specification requirements\n- **Snapshot validation**: Ensure snapshots match specification exactly\n- **Approval tests**: Use approval tests to catch spec violations\n\n## Quality Checklist\n\n### Before Finalizing Specification\n\n- [ ] All requirements clearly stated\n- [ ] Examples provided for complex requirements\n- [ ] Error cases covered\n- [ ] Cross-references to other specs included\n- [ ] Out of scope items clearly defined\n- [ ] Testing requirements specified\n- [ ] Consistent formatting throughout\n- [ ] Check the generated Document with `markdownlint` (if available), apply\n auto-fixes and fix the remaining issues manually.\n\n### Review Criteria\n\n- [ ] Is the specification unambiguous?\n- [ ] Are all edge cases covered?\n- [ ] Does it integrate well with other specs?\n- [ ] Is it testable?\n- [ ] Is it maintainable?\n\n## Common Pitfalls to Avoid\n\n### Ambiguity\n\n- **Vague language**: \"The system should handle errors gracefully\"\n- **Missing details**: Not specifying exact error handling behavior\n- **Unclear relationships**: Not explaining how components interact\n\n### Inconsistency\n\n- **Different terms**: Using different terms for the same concept\n- **Inconsistent formatting**: Not following established patterns\n- **Conflicting requirements**: Requirements that contradict other specs\n\n### Incompleteness\n\n- **Missing edge cases**: Not considering unusual scenarios\n- **Incomplete examples**: Examples that don't cover all cases\n- **Missing error handling**: Not specifying what happens when things go wrong\n\n## Related Rules\n\n- [spec-compliance-investigation.mdc](mdc:.cursor/rules/spec-compliance-investigation.mdc)\n How to investigate spec-implementation discrepancies\n- [base_overview.mdc](mdc:.cursor/rules/base_overview.mdc) Project structure and\n conventions\n\nAs it is obvious this is a very intricate rule with many criteria. For this you really need a reasoning and deep-thinking model that can also reason for extended times (many minutes are normal!) and call tools every now and then to get even more information. Models like o3, deepseek-r1 and the opus-series of claude really shine here.\n\n\nSpec Compliance\n\n---\ndescription: Spec Compliance Investigation Guide\nglobs:\nalwaysApply: false\n---\n\n# Spec Compliance Investigation Guide\n\n## Overview\n\nThis rule provides a systematic approach for investigating discrepancies between\nspecifications and implementations, following the pattern established. Do not\nchange any code during this phase.\n\n## Investigation Process\n\n### 1. Initial Analysis\n\n- **Locate specification**: Find the relevant spec file in [specs/](mdc:specs)\n- **Identify implementation**: Find corresponding source code in [src/](mdc:src)\n- **Check tests**: Review test files in [tests/](mdc:tests) for expected\n behavior\n- **Run tests**: Execute `poetry run pytest` to identify current failures\n\n### 2. Systematic Comparison\n\nFor each specification section:\n\n1. **Extract requirements** from spec file\n2. **Examine implementation** in source code\n3. **Compare outputs** with test snapshots\n4. **Document discrepancies** with specific examples\n\n### 3. Documentation Structure\n\nCreate analysis document in [tmp/spec\\_[component]\\_discrepancies.md](mdc:tmp/)\nwith:\n\n```markdown\n# [Component] Specification vs Implementation Discrepancies\n\n## Executive Summary\n\nBrief overview of findings and impact\n\n## Key Discrepancies Found\n\n### 1. [Category] - [Specific Issue]\n\n**Specification:**\n\n- Requirement details\n\n**Implementation:**\n\n- Current behavior\n- ✅ Correct aspects\n- ❌ Incorrect aspects\n\n## Test Results\n\n- Current test failures\n- Output differences\n\n## Impact Assessment\n\n### High Impact Issues:\n\n- Critical functionality problems\n- User experience issues\n\n### Medium Impact Issues:\n\n- Consistency problems\n- Formatting issues\n\n### Low Impact Issues:\n\n- Minor differences\n- Style variations\n\n## Recommendations\n\n### Option 1: Update Spec to Follow Code\n\n**What to change:**\n\n- Specific spec modifications\n\n**Pros:**\n\n- Benefits of this approach\n\n**Cons:**\n\n- Drawbacks of this approach\n\n### Option 2: Update Code to Follow Spec\n\n**What to change:**\n\n- Specific code modifications\n\n**Pros:**\n\n- Benefits of this approach\n\n**Cons:**\n\n- Drawbacks of this approach\n\n### Option 3: Recommended Hybrid Approach\n\n**Recommended Solution:**\n\n- Phased implementation plan\n\n**Rationale:**\n\n- Why this approach is best\n\n**Implementation Priority:**\n\n- Immediate, short-term, medium-term tasks\n```\n\n## Quality Checklist\n\n### Before Finalizing Investigation\n\n- [ ] All specification sections reviewed\n- [ ] Implementation code thoroughly examined\n- [ ] Tests run and failures documented\n- [ ] Impact assessment completed\n- [ ] All three solution options evaluated\n- [ ] Recommendation justified with rationale\n- [ ] Implementation plan prioritized\n\n### Documentation Quality\n\n- [ ] Specific examples provided for each discrepancy\n- [ ] Code snippets included where relevant\n- [ ] Pros/cons clearly articulated\n- [ ] Implementation steps detailed\n- [ ] Priority levels assigned\n\n## Related Rules\n\n- [Base Project Overview](mdc:.cursor/rules/base-project-overview.mdc) Project\n structure and conventions\n- [Spec Guidelines](mdc:.cursor/rules/spec-guidelines.mdc) How to write\n specifications\n\nThis compliance-report also need a deep-thinking model, like the specification beforehand.\n\n\nTasks\n\n---\ndescription:\nglobs:\nalwaysApply: false\n---\n\n## Task List creation\n\n### Purpose\n\nTranslate a Product Requirements Document (PRD) into an executable Markdown task\nlist that a junior developer (human or AI) can follow without extra context.\n\n### Output\n\n- Create /tasks/<feature>/TASKS.md (overwrite if it exists).\n- Markdown only, no prose around it.\n- Epics = H2 headings (`## 1. <Epic>`).\n- Tasks = unchecked check‑boxes (`- [ ] 1.1 <task>`).\n- Sub‑tasks = indent one space under their parent (` - [ ] 1.1.1 <subtask>`).\n- Create a /tasks/<feature>/Task*<Epic>*<task>\\_<subtask>.md (i.e.\n `Task_3_2_4.md` for Epic 3, Task 2, Subtask 4)\n\n### Process\n\n1. Read the tagged PRD.\n2. **Investigate** the current state of the repository to collect answers to\n your first questions. All specs for fixed behaviours and outputs are located\n in `specs/`. **Consult those** as a source first before trying to\n reverse-engineer from the code. \n If specs are in need of change then this is also a task to be generated.\n3. If critical info is missing and cannot be answered by looking at the code,\n ask max five clarifying questions (Q1 … Q5) and stop until answered.\n4. After questions are answered think about the answers and: Either: look at the\n code again, then goto 3., and ask for further clarification Or: Reply\n exactly: Ready to generate the subtasks – respond **go** to proceed.\n5. On a user message that contains only the word \"go\" (case‑insensitive): a.\n Generate /tasks/<feature>/TASKS.md following _Output_ spec. b. Reply with:\n TASKS.md created – review them.\n6. After TASKS.md was reviewed, create `Task_<e>_<t>_<s>.md` for each task and\n subtask containing implementation hints like relevant specs (link them!),\n primary files to edit/review for this task, tests needing change, etc.\n7. Stop. Do **not** begin executing tasks in this rule.\n\n### Writing guidelines\n\n- Each item ≤120 characters, start with an action verb.\n- Hints are allowed below each item as HTML-Comment and do not count against the\n 120 characters.\n- Group related work into logical epics with ≤7 direct child items.\n- Prefer concrete file paths, commands, specs or APIs when available.\n- Skip implementation details obvious from the codebase in the overview.\n- If a task only concerns up to 5 files, name them in the detailed file.\n Otherwise give hints on how to search for them (i.e. \"everything under\n `src/models/`\").\n\n### Safety rails\n\n- Never touch production data.\n- Assume all work happens in a feature branch, never commit directly to main.\n- Check the generated Document with `markdownlint` (if available), apply\n auto-fixes and fix the remaining issues manually.\n\nThis also works better with one of those deep-thinking models.\n\n\nOther Rules\nI have some other rules with guidelines on how to write proper tests, one for “just follow the tasks in TASKS.md one by one until done with commit after each task”, etc. Those are omitted for brevity. Also they are so simple, that non-reasoning-models can follow them. Cheap claude3-sonnet or small, specialised coding-LLMs are enough to get the job done with this preparation.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
- "objectID": "Writing/documentation.html#was-macht-eine-gute-dokumentation-aus",
- "href": "Writing/documentation.html#was-macht-eine-gute-dokumentation-aus",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Was macht eine gute Dokumentation aus",
- "text": "Was macht eine gute Dokumentation aus\n\nFormelle Prinzipien: Open-Source-Research, FAIR4RS und ENDINGS\nBeachten Sie, dass dieser Anforderungskatalog in Einklang mit den Prinzipien des Research Software Engineering[1] und den FAIR4RS-[4] bzw. ENDINGS-Prinzipien[3] steht.\n\n\n\n\n\n\n\nFAIR4RS-Prinzipien für Software\n\n\n\nDie FAIR4RS-Prinzipien sind eine Anpassung der Ursprünglich nur für Daten gedachten FAIR-Prinzipien. Der Fokus liegt hier nicht auf Software selbst, sondern auf eine Nutzung von Software die ein Äquivalent zur Nutzung von FAIR-Daten darstellt.\n\n\n\n\n\n\n\nENDINGS-Prinzipien\n\n\n\nDie ENDINGS-Prinzipien für digitale Projekte betonen insbesondere die Bedeutung von Dokumentation für Datenstrukturen, offenen Lizenzen, statischen Outputs und Zitierbarkeit.\n\n\nGute Dokumentation bedeutet daher u.a. die Verdeutlichung und Sicherstellung von\n\nReproduzierbarkeit (Installation, Daten, Beispiele),\nOffenheit (Lizenz, offene Formate) und\nNachhaltigkeit (Versionierung, Langlebigkeit der Doku).\n\nIndem Sie also diesem Anforderungskatalog folgen, berücksichtigen Sie automatisch wichtige anerkannte Prinzipien für gute wissenschaftliche Softwarepraxis.\n\n\nNutzungshilfen außerhalb der Dokumentation\nFalls Ihre Software ein Command-Line Interface (CLI) hat, stellen Sie sicher, dass eine eingebaute Hilfe vorhanden ist (z. B. Ausgabe bei --help). Viele Nutzer greifen zunächst darauf zurück. Dieses Hilfemenü sollte kurz erläutern, welche Subkommandos oder Optionen existieren. Moderne CLI-Frameworks generieren solche Hilfen oft automatisch aus Ihrem Code (z. B. argparse in Python erzeugen --help-Texte). Nutzen Sie das, um konsistente Infos zu garantieren.\nFür GUI-Anwendungen sollten Tooltips, Hilfetexte in der Oberfläche oder zumindest ein kleiner Help-Abschnitt im Handbuch vorhanden sein. Diese eingebetteten Hilfen ersetzen keine ausführliche Dokumentation, aber sie senken die Schwelle für alltägliche Fragen.\n\n\nKontinuierliche Verbesserung und Feedback\nDokumentation ist kein einmaliges Ereignis, sondern ein fortlaufender Prozess. Best Practice sind daher insbesondere:\n\nfrüh Feedback von Testnutzer*innen oder Kolleg*innen einzuholen: Lassen Sie jemanden die Anleitung befolgen und hören Sie auf Stolpersteine. Oft zeigen sich Lücken erst im Praxistest (“Ich wusste nicht, was ich nach Schritt X tun soll” etc.).\nPlanen Sie Zeiten ein, die Dokumentation nachzuführen, insbesondere wenn sich die Software ändert. Ein lebendiges Projekt wird vielleicht Release für Release die Dokumentation erweitern (evtl. neue Tutorials, neue Module dokumentieren). Spätestens zum Release-Zeitpunkt sollten diese auffallen und ggf. als Issues adressiert werden.\nNutzen Sie auch Issues für Dokumentation: Wenn Nutzer Fragen stellen, überlegen Sie, ob die Antwort in die offizielle Doku übernommen werden sollte. So wächst die Dokumentation organisch entlang der tatsächlichen Bedürfnisse.\n\n\n\nPositiv- und Negativbeispiele studieren\nSchlussendlich ist ein guter Weg, die eigene Dokumentation zu verbessern, ist ein Blick auf Projekte mit exzellenter Doku. Im Journal of Open Source Software (JOSS) werden z.B. Softwareartikel veröffentlicht, bei denen die zugehörigen Repositorien aufgrund des Review-Prozesses vorbildliche READMEs und Wikis haben. Diese können als Vorlage dienen.\nNutzen Sie solche Ressourcen; sie ersparen einem das Rad neu zu erfinden. Allerdings: Adaptieren Sie sie auf Ihre Bedürfnisse – nicht jede Vorlage passt 1:1.",
+ "objectID": "Writing/coding-age-ai.html#example-rules-in-action",
+ "href": "Writing/coding-age-ai.html#example-rules-in-action",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
+ "section": "Example: Rules in Action",
+ "text": "Example: Rules in Action\nThe codebase we look at here is a project called gitlab_overviewer. It takes GitLab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in GitLab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.\nThe current issue is, that due to a complete rewrite we are happy with the current output, but there are some minor problems still remaining. Some of them may also due to specs being incorrect, because requirements or ideas changed over time.\n\nAnalyzing the situation\nFor the analysis o3 was used and it was sufficient on first try. Here is (part of) the conversation:\n\n\n\n\n\n\nUser\n\n\n\n@spec-compliance-investigation.mdc do a spec-compliance investigation on @spec_renderer_markdown.md . The relevant modules in the @/src should link to the spec (omissions are possible and also out-of-spec).\n\n\n\n\n\nThe initial analysis with most thinking steps opened. It is interesting to see the agent collect needed information onthe first try and generates a lot of ‘thought’.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\ntmp/spec_renderer_markdown_discrepancies.md:\n\nMarkdown Renderer Specification vs Implementation Discrepancies\nExecutive Summary\nThe current render_grouped_markdown implementation provides most of the layout and structural requirements defined in specs/spec_renderer_markdown.md. However, several key aspects diverge from the specification, leading to failing approval tests and user-visible issues. The most critical problems concern ordering guarantees and the content chosen for the Description callout.\nKey Discrepancies Found\n1. Ordering – Projects inside detailed sections\nSpecification:\n\nProjects in the detailed part “appear in the same order they had inside their group’s summary table” (Spec §4, bullet 3).\n\nImplementation:\n# ... excerpt ...\nfor od in sorted(group_projects, key=lambda p: p.project.name.lower()):\n # renders project sections alphabetically\n\nProjects are sorted alphabetically, not in the original summary-table order.\n✅ Groups themselves are ordered by descending project count (matches spec).\n❌ Project order within groups breaks the “mirror the incoming, already-sorted data” rule.\n\n2. Description callout picks raw front-matter if no description key exists\nSpecification:\n\nIf front-matter contains a description key → use its value (callout-type tip).\nElse if Readme.content is not empty → use that content (callout-type warning).\nReadme.content is defined as the first real paragraph without front-matter (see Model spec).\n\nImplementation:\n\nCorrectly checks for .raw_frontmatter[\"description\"] first ⬆️.\nThen falls back to od.readme.content unconditionally. In practice many fixture READMEs still include --- … YAML because the pre-processing step did not strip it, so the callout now contains the entire front-matter block – cf. failing snapshot diff lines starting with > --- \"author\":.\n✅ Callout types (tip/warning/danger) are chosen per spec.\n❌ Content for the warning variant violates the “no raw front-matter” expectation and visual quality.\n\n8< ……… 8<\nTest Results\npoetry run pytest -q → 5 failures / 119 passed\n • tests/rendering/test_markdown_renderer_approval.py (output mismatch)\n • tests/rendering/test_quarto_renderer_approval.py (cascade – shares ordering logic)\n • tests/models/test_model_invariants.py (3 failures unrelated to renderer)\nThe Markdown approval diff highlights exactly the two major discrepancies above (alphabetical ordering & raw front-matter in callout).\nImpact Assessment\nHigh Impact Issues\n\nIncorrect project ordering – breaks reproducibility guarantees and user expectations; also propagates to Quarto renderer.\nRaw YAML in description callout – severely harms readability of generated documentation.\n\nMedium Impact Issues\n\nNone identified besides cosmetic phrasing.\n\nLow Impact Issues\n\nMinor stylistic deviations (e.g., spacing) appear compliant within tolerance.\n\nRecommendations\nOption 1: Update Spec to Follow Code\nWhat to change:\n\nRelax ordering requirement to alphabetical order.\nAllow raw front-matter YAML in description callout.\n\nPros: No code changes; quick test green.\nCons: Violates original design intent; exposes low-level data to end-users; poor UX.\nOption 2: Update Code to Follow Spec (preferred)\nWhat to change:\n\nRemove the sorted(...) call to preserve input ordering.\nEnsure Readme.content truly excludes YAML front-matter (use parse_readme during data ingestion or strip before rendering).\n\nPros: Restores spec fidelity; fixes approval tests; better documentation quality.\nCons: Requires code edit & possibly data-loading adjustments.\nOption 3: Hybrid Approach\n\nFix ordering in renderer.\nKeep current fallback but truncate front-matter to first paragraph until improved extraction available.\n\nRecommended Solution: Option 2 – small, well-scoped patches bring implementation back in line with spec and tests.\nImplementation Priority:\n\nImmediate: ordering fix & front-matter stripping.\nShort-term: add regression tests for ordering.\nMedium-term: audit other renderers (Quarto) for same issues.\n\n\nI have completed the investigation phase and produced a detailed report at\ntmp/spec_renderer_markdown_discrepancies.md\nKey take-aways:\n• Two core deviations from the Markdown-renderer spec were found\n\nProject ordering in the detailed section is alphabetical instead of mirroring the summary order.\nWhen a README lacks a description: key, the Description call-out falls back to raw front-matter, so YAML blocks are rendered verbatim.\n\n• These issues are the root cause of the failing approval tests.\n• A concise impact assessment and three solution options are documented; the recommended path is to fix the code to match the specification.\nLet me know if you’d like to proceed with the recommended fixes or explore the alternate options.\n\n\n\n\n\nCorrecting the analysis\nBy reading the analysis i soon noticed, that things were missing out of the spec. So i added a bit of text:\n\n\n\n\n\n\nCommenting the result\n\n\n\n\n1. Ordering – Projects inside detailed sections\nSpecification: > […snip…]\n\n❌ Project order within groups breaks the “mirror the incoming, already-sorted data” rule.\n\nComment:\n\nThe implemented behaviour is using alphabetical order in the detailed section and in the table according to the sort defined in the @spec_table_config . The spec needs updating.\n\n\n\n2. Description callout picks raw front-matter if no description key exists\nSpecification: > […snip…]\n\n❌ Content for the warning variant violates the “no raw front-matter” expectation and visual quality.\n\nComment:\n\nThis is a violation of spec. But additionally it should only render the first 10 lines of Readme.content and then add a …continues… if there are more lines.\n\n\n\n\nArmed with those correction i told the LLM to generate better recommendations, that align with my goals. The LLM then deleted the whole recommendations-section and wrote exactly what i wanted. Again o3 was used for the slow and deep thinking.\n\n\n\n\n\n\nUser\n\n\n\nI added comments to the part that are not up to spec. Update the recommendations in @spec_renderer_markdown_discrepancies.md according to those comments.\n\n\n\n\n\nThoughts of the agent after i updated the comments. It always feels good to read that i was understood3 by the machine.\n3 yaya .. i know. Antropomorphing an AI.\n\n\n\n\n\n\n\n\nCursor\n\n\n\ntmp/spec_renderer_markdown_discrepancies.md:\n\n// … existing code …\nRecommendations\nOption A: Align Specification with Current Ordering (preferred)\nWhat to change:\n\nAmend spec_renderer_markdown.md §4 to state that projects in the detailed section appear alphabetically by project name. This legitimises the current implementation and removes the ordering-related approval failure.\nKeep the existing implementation for project ordering unchanged.\nChange the renderer implementation for the Description call-out:\n\nStrip YAML front-matter from Readme.content (use parse_readme).\nRender at most the first 10 non-empty lines of the resulting text.\nIf more lines remain, append a marker …continues… as the final line inside the call-out.\n\nAdd corresponding wording to the spec clarifying the 10-line truncation rule.\n\nPros:\n\nLeast intrusive to working behaviour users already rely on.\nFixes readability issue without reordering content.\nSmaller diff in test fixtures (only call-out content).\n\nCons:\n\nRequires a spec update (but that is a documentation-only task).\n\nOption B: Keep Original Spec Ordering, Change Code\nWhat to change:\n\nRemove sorted() so project order mirrors summary-table order.\nApply the same Description call-out improvements as in Option A.\n\nPros:\n\nUpholds original design intent of summary/detailed symmetry.\n\nCons:\n\nGenerates a larger diff for existing artefacts; may surprise current users.\n\nRecommended Solution\nProceed with Option A:\n\nUpdate the specification paragraph on ordering.\nImplement the 10-line truncation + …continues… logic in the renderer.\n\nImplementation Priority\n\nImmediate: implement call-out truncation logic & adjust approval snapshots.\nImmediate: update Markdown renderer spec for alphabetical ordering and call-out truncation.\nShort-term: add regression tests for the new truncation rule.\nMedium-term: propagate the truncation rule to the Quarto renderer to keep output parity. // … existing code …\n\n\nThe “Recommendations” section has been rewritten to reflect your comments:\n1. Ordering: keep the current alphabetical behaviour and adjust the spec instead.\n2. Description call-out: implement a 10-line preview with a “…continues…” marker after stripping front-matter.\nThese points are now captured as Option A (preferred), with a revised implementation roadmap.\nLet me know when you’d like me to start on the code/spec changes.\n\n\n\n\n\nCreating Tasks\nNow that we have a clear plan without touching any code, lets flesh out the Details.\n\n\n\n\n\n\nUser\n\n\n\n@generate-tasks.mdc generate Tasks for Option A of @spec_renderer_markdown_discrepancies.md under a feature called “markdown_rendering”.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\nTASKS.md:\n\n1. Spec updates\n\n1.1 Amend alphabetical ordering rule in specs/spec_renderer_markdown.md\n1.2 Document 10-line truncation rule for Description call-out\n\n2. Markdown renderer implementation\n\n2.1 Strip YAML front-matter before rendering description\n\n2.1.1 Invoke parse_readme to obtain cleaned content\n2.1.2 Add unit test ensuring YAML is absent in call-out\n\n2.2 Truncate description to first 10 lines with …continues…\n\n2.2.1 Implement truncation logic in render_grouped_markdown\n2.2.2 Update approval snapshot for Overview.md\n\n2.3 Verify alphabetical ordering comment references spec\n\n3. Quarto renderer alignment\n\n3.1 Port truncation & YAML-stripping logic to src/gitlab_overviewer/rendering/quarto.py\n3.2 Refresh Quarto renderer snapshot & tests\n\n4. Tests & snapshots\n\n4.1 Update Markdown renderer approval snapshot after changes\n4.2 Update Quarto renderer snapshot after changes\n4.3 Add regression test for 10-line truncation rule\n\n5. Documentation & housekeeping\n\n5.1 Run markdownlint and fix issues in updated docs/specs\n5.2 Add entry to CHANGELOG.md under Unreleased\n\n\nTask_1_1.md:\n\nTask 1.1 – Amend alphabetical ordering rule in spec_renderer_markdown.md\nRelevant spec: specs/spec_renderer_markdown.md (§4 Ordering Guarantees)\nGoal: Replace the bullet “projects appear in the same order they had inside their group’s summary table” with “projects appear alphabetically by their name”.\nPrimary files:\n\nspecs/spec_renderer_markdown.md\n\nHints:\n\nSearch for the existing ordering clause (§4 bullet 3) and update accordingly.\nRun markdownlint after editing: npx markdownlint-cli2 specs/spec_renderer_markdown.md --fix.\n\n\nTask_1_2.md:\n\nTask 1.2 – Document 10-line truncation rule for Description call-out\nRelevant spec: specs/spec_renderer_markdown.md (§3 Project Detail Sections)\nGoal: Add a subsection clarifying that when the Description call-out uses Readme.content, the renderer must:\n\nStrip YAML front-matter.\nRender the first 10 non-empty lines.\nAppend …continues… if more content was omitted.\n\nPrimary files:\n\nspecs/spec_renderer_markdown.md\n\nHints:\n\nPlace this clarification right after rule 3.1 in §3.\nProvide a small Markdown example block.\nRemember to update any cross-links.\n\n\n[…further tasks omitted for brevity…]\n\n\n\n\n\nExecuting\nWith all that detailed instructions changes to the code are narrowly focused and the changes are easily reviewable. Execution does not need any deep thinking and can be done by a fast and cheap code-model like gemini-2.5, claude3-sonnet, etc.\nThey usually just loads all relevant files as context, work step-by-step, verify, tick off the task and report back for review.\n\n\n\n\n\n\nUser\n\n\n\n@xxx-work-on-tasks.mdc @/markdown_rendering Implement all Task1\\*.md.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\nI’ll help you implement the tasks from Task1*.md in the markdown rendering feature. Let me first check what these tasks entail.\nLet me first check the current state of the spec file we need to modify.\nI’ll now implement both tasks. Let’s start with Task 1.1 - amending the alphabetical ordering rule.\n// ... existing code ...\n\n## 4. Ordering Guarantees\n\n- Groups are ordered by the number of Projects rendered (alphabetically on a\n tie).\n- Summary project order mirror the incoming, already-sorted data (see\n [Table Sorting](./spec_table_sorting.md)).\n- In the detailed part, projects appear alphabetically by their name.\n\n// ... existing code ...\nNow let’s implement Task 1.2 - documenting the 10-line truncation rule for the Description call-out.\n// ... existing code ...\n\nMissing entries from the Project-Info are omitted.\n\nWhen using `Readme.content` in a Description call-out, the renderer must:\n\n```markdown\n1. Strip any YAML front-matter from the content\n2. Include only the first 10 non-empty lines\n3. If content was truncated, append \"…continues…\" on a new line\n```\n\nExample:\n\n```markdown\n> [!warning] Description\n>\n> # My Project\n>\n> This is a great project that does many things. It has multiple features:\n>\n> - Feature 1\n> - Feature 2\n> - Feature 3\n> - Feature 4 …continues…\n```\n\n2. **Release badge** – image linking to `<web_url>/-/releases`. // ...\n existing code ...\nLet me run markdownlint as suggested in the task hints to ensure our changes meet the markdown style guidelines.\nI’ve completed both tasks:\n\nTask 1.1: Updated the ordering rule in section 4 to specify that projects appear alphabetically by their name in the detailed part, replacing the previous rule about maintaining summary table order.\nTask 1.2: Added documentation about the Description call-out truncation rules after section 3.1, including:\n\nThe three required steps (strip YAML front-matter, render first 10 non-empty lines, append “…continues…”)\nA clear example showing how the truncation should look\n\n\nThe markdownlint output shows some line length issues and a heading issue, but these appear to be pre-existing in the file and not related to our changes. The changes we made maintain the existing formatting style of the document.\nWould you like me to address any of the markdownlint issues, or would you prefer to keep the changes focused on just implementing the two tasks?\n\n\n\nAnd within 1 Minute everything was updated as expected and verified within a few seconds. Here you can really see the advantages of this approach. Instead of going off-track and focusing i.e. on fixing the markdownlint-issues4 the agent notes that this is still an issue, but offers to keep focused on the tasks for the moment. With a simple ‘go’ i can continue tackle the next thing. 5 Minutes later everything is done and we have one less issue.\n4 As i have often expirienced with LLMs trying to be too perfect and pleasing the user without ever stopping",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
- "objectID": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
- "href": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "(Teil-)automatisierte Dokumentationswerkzeuge",
- "text": "(Teil-)automatisierte Dokumentationswerkzeuge\nDie Dokumentationslast lässt sich durch den Einsatz geeigneter Werkzeuge erheblich senken. Gerade Forschende, die alleine programmieren, können von (teil-)automatisierter Dokumentation profitieren, um konsistente und aktuelle Unterlagen zu erhalten, ohne alles von Hand schreiben zu müssen. Im Folgenden werden einige Tools und Möglichkeiten vorgestellt – samt Empfehlungen, wann ihr Einsatz sinnvoll oder notwendig ist:\n\nJupyter Notebooks und literate programming\nEin mächtiges Werkzeug – gerade in datengetriebenen Geisteswissenschaften – sind Jupyter Notebooks bzw. R Markdown Notebooks [8]. Diese erlauben es, ausführbaren Code mit erklärendem Text und Visualisierungen in einem Dokument zu vereinen. Für Dokumentationszwecke können Notebooks zweierlei leisten:\n\nals Tutorials/Beispiel-Workflows, die Nutzer interaktiv nachvollziehen können, und\nals Reproduzierbarkeits-Dokumentation für analytische Prozesse.\n\nWenn Ihre Forschungssoftware z. B. eine Bibliothek ist, könnten Sie ein Notebook bereitstellen, das einen typischen Anwendungsfall durchspielt (inklusive Daten-Loading, Aufruf der Funktionen, Darstellung der Ergebnisse).\nNotebooks senken die Hürde, weil Nutzer direkt experimentieren können, und fördern transparente Forschung, da Code, Ergebnisse und Beschreibung zusammenfließen. Sie sind daher sinnvoll, wenn der Hauptanwendungsfall die Durchführung von Analysen oder Datenverarbeitungen ist, die man Schritt für Schritt demonstrieren kann.\n\n\n\n\n\n\nVorsicht bei Python-Notebooks\n\n\n\nNotebooks erfordern eine lauffähige Umgebung – das heißt, Sie müssen darauf achten, dass alle Abhängigkeiten im Notebook deklariert sind oder ggf. nachinstalliert werden und die Daten zugänglich sind.\nEs hat sich gezeigt, dass Notebooks aus Publikationen oft nicht ohne Weiteres laufen, weil Pfade, Datenquellen oder spezielle Umgebungen fehlen.\nDeshalb: Wenn Sie Notebooks als Doku nutzen, stellen Sie sicher, dass sie leicht ausführbar sind (z. B. durch Bereitstellen von Umgebungsdateien wie environment.yml oder Dockerfiles, kleinen Beispieldatensätzen und klaren Anweisungen im Notebook). Ggf. kann man zusätzlich auch reine Markdown/HTML-Exporte von evaluierten Notebooks exportieren und dem Repo beilegen, damit zumindest statisch die Inhalte einsehbar sind.\n\n\n\nWann sollten Sie Notebooks nutzen?\nNotebooks sind quasi Goldstandard, um wissenschaftliche Analysen nachvollziehbar zu machen. In Projekten, wo es um Data Science Workflows oder interaktive Exploration geht, sollten Notebooks stark erwogen werden, während für ein reines Tool/Script eine gut geschriebene README mit Beispielausgabe ausreichend sein kann.\n\n\n\nSphinx/MkDocs/Doxygen (statische Dokumentationswebseiten)\n\n\n\n\n\n\n\nPrinzip\n\n\n\nAb einer Codebasis > einige tausend Zeilen oder >5 nontriviale Module sollte eine generierte Dokumentation bereitstehen.\n\nFür umfangreichere Projekte oder solche mit eigener Website kann es sinnvoll sein, eine Dokumentationswebsite zu generieren. Tools wie Sphinx (zusammen mit ReadTheDocs für Hosting) oder MkDocs erlauben es, aus Markdown/reStructuredText-Dateien einen ansprechend formatierten HTML-Dokumentationssatz zu bauen. Der Vorteil ist, dass man eine durchsuchbare, verlinkte Doku bekommt, oft mit schönem Layout und zusätzlicher Navigation. Mit Continuous Integration lassen sich diese Seiten bei jedem Git-Push automatisch aktualisieren.\nFür die Nachhaltigkeit ist wichtig, dass diese Webseiten statisch sind[3] – d.h. sie funktionieren ohne Server-Backends und bleiben auch offline nutzbar.\nSolche Tools sind sinnvoll, wenn die Dokumentation sehr groß oder öffentlich weit verbreitet ist – z. B. wenn Ihre Software von vielen genutzt wird und Sie ein professionelles Auftreten wünschen, oder wenn Sie die Doku als PDF veröffentlichen möchten. In kleinen DH-Projekten ist es oft nicht nötig, extra eine Webseite zu hosten; dennoch kann Sphinx auch lokal HTML/PDF erzeugen, was man dem Repo beilegen kann.\n\nWann sollten Sie eine statische Website generieren?\nVerpflichtend ist so ein Tool selten, höchstens wenn Förderprogramme oder Journals ein dokumentationsseitiges HTML-Manual verlangen. Wenn Sie jedoch planen, Ihre Software z. B. über Jahre zu pflegen und ggf. einem Journal wie JOSS vorzustellen, dann erwartet die Community meist, dass zumindest eine Sphinx/Doxygen-Doku für die API (s.u.) existiert.\n\n\n\nDocstrings und API-Dokumentationsgeneratoren\n\n\n\n\n\n\n\nPrinzip\n\n\n\nBenutzt jemand die Software nur, braucht es keine API-Dokumentationen; wird die Software aber woanders eingebunden, ist dieses notwendig. Generation dieser Dokumentation ist daher der beste Weg.\n\n5 kurz für: “Documentation String”Nutzen Sie die Möglichkeit, Dokumentation direkt im Quellcode unterzubringen, z. B. in Form von Docstrings5 (mehrzeilige Strings in Funktionen/Klassen bei Python, Roxygen-Kommentare in R, Javadoc-Kommentare in Java, etc.).\nDiese dienen doppelt: Zum einen erleichtern sie es Ihnen und Kollegen, den Code beim Lesen zu verstehen, zum anderen können sie von Tools ausgelesen und zu hübschen API-Dokumentationen verarbeitet werden. Idealerweise dokumentieren Sie jede wichtige oder von außen sichtbare Funktion, Klasse oder Modul mit einem kurzen Docstring, der Zweck, Parameter, Rückgaben und ggf. Beispiele enthält. Für kleine Scripte genügen ggf. Modul- oder Abschnittskommentare.\nWichtig ist Konsistenz im Stil – halten Sie sich an Konventionen Ihres Ökosystems (z. B. Google Style Guide für Python Docstrings oder entsprechende Formatvorgaben für andere Sprachen)[9]. Verlinken sie diese Styleguides in der README. Sogenannte Linting-Tools, wie etwa pylint, können die Verwendung erzwingen.\nMit Tools, wie Sphinx, Javadoc, Doxygen, MkDocs,pdoc und vielen weiteren, können aus Docstrings automatisiert Webseiten oder PDF-Handbücher generiert werden. Sie lesen z. B. die Python-Docstrings und erzeuge daraus strukturiert eine Dokumentation; Häufig kann über Erweiterungen auch dritte Dokumentation direkt eingebunden und verlinkt werden.\n\n\nVersionskontrolle und kontinuierliche Dokumentationspflege\n\n\n\n\n\n\n\nPrinzip\n\n\n\nDie beste Dokumentation ist die, die sich selbst aktualisiert.\n\nEine Form der Teil-Automatisierung ist es, die Dokumentation an den Entwicklungs-Workflow zu koppeln. So sollte die Dokumentation im selben Versionskontrollsystem (Git) liegen wie der Code, damit Änderungen synchron nachverfolgt werden. Es empfiehlt sich, bei jedem größeren Code-Update zu prüfen, ob die Doku noch stimmt (das kann man sich z. B. als Punkt in Pull-Request-Reviews notieren oder per Issue-Template abfragen). Für Projekte mit Continuous Integration (CI) kann man sogar automatisierte Checks einrichten, die z. B. prüfen, ob die Doku gebaut werden kann oder ob Docstrings fehlen. Einige CI-Skripte generieren bei jedem Commit eine frische Doku (z. B. mittels Sphinx) und veröffentlichen sie – so ist garantiert, dass die aktuelle Codeversion immer eine aktuelle Doku hat.\nSchlussendlich muss aber das Level an Automation für jedes Projekt individuell abgewogen werden.",
+ "objectID": "Writing/coding-age-ai.html#result",
+ "href": "Writing/coding-age-ai.html#result",
+ "title": "Field Report: Coding in the Age of AI with Cursor",
+ "section": "Result",
+ "text": "Result\nIn the fast-evolving field of AI-assisted software development, my experience with Cursor’s specification-driven workflow has yielded tangible outcomes:\n\nCode Quality: By enforcing strict compliance checks and structured task creation, redundant code generation was reduced. The LLM still gets excited to “help”, but now it’s actually contributing meaningful changes instead of reinventing the wheel.\nDevelopment Efficiency: The PRD-first approach has cut try&error time significantly. Before, getting the things i wanted was hit&miss. Now i “read the agents mind” and correct it during specification-creation.\nProject Integrity: Through rigorous spec adherence and systematic task breakdown, the codebase is more consistent, much better documented and decently tested5.\nReduced Cognitive Load: The Cursor rule system has made it easier to focus on actual development tasks instead of wrestling with the AI’s sometimes questionable assumptions - or just installing a third different testing framework for exactly this module.\n\n5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the foreseeable future, so if you are looking for a project, feel free!\nAll in all this isn’t a silver bullet for all AI-assisted development problems, but it’s made my coding experience with Cursor much more productive and predictable. It turns out treating an AI as a slightly overeager junior developer who needs clear instructions works better than hoping it’ll just “do the right thing”.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
- ]
- },
- {
- "objectID": "Writing/documentation.html#checklisten-und-vorlagen",
- "href": "Writing/documentation.html#checklisten-und-vorlagen",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Checklisten und Vorlagen",
- "text": "Checklisten und Vorlagen\nUm zu entscheiden, was dokumentiert wird (und was nicht), helfen etablierte Best Practices sowie Vorlagen aus der Community. Im Folgenden sind einige bewährte Richtlinien zusammengefasst.\n\nCheckliste für die Mindest-Dokumentation\nDie folgenden Punkte fassen zusammen, was eine gute Dokumentation mindestens enthalten sollte. Sie können auch als Qualitäts-Checkliste dienen, um Ihre Dokumentation zu überprüfen:\n\nZielklärung: Ist der Zweck der Software klar benannt und der wissenschaftliche Need begründet? (Falls nein, ergänzen: Warum existiert dieses Tool?)\nInstallation & Voraussetzungen: Sind alle Schritte, um die Software lauffähig zu machen, dokumentiert (inkl. Dependencies, evtl. mit Installationsbefehlen)? Ist ersichtlich, welche Umgebung nötig ist (OS, Hardware)?\nGrundlegende Nutzung: Gibt es eine Anleitung oder Beispiele, wie man die Software verwendet (Eingabe -> Ausgaben)? Ist mindestens ein typischer Workflow beschrieben, idealerweise mit Beispielinput und -output?\nOptionen & Schnittstellen: Falls relevant – sind alle wichtigen Funktionen, Befehlsoptionen oder API-Methoden dokumentiert? (Nicht unbedingt jede intern, aber alles, was ein Nutzer aufrufen könnte). Für APIs: Sind Parameter und Rückgaben erläutert?\nValidierung & Einschränkungen: Werden Annahmen und Grenzen der Software genannt? Weiß ein*e Nutzer*in, welche Fälle nicht abgedeckt sind oder worauf zu achten ist (z. B. Datenqualität, maximale Größen)? Transparenz hier verhindert Frustration.\nHintergrund & Referenzen: Sind die wichtigsten konzeptionellen Hintergründe oder Referenzen angegeben? (Z. B. theoretische Grundlagen, Algorithmen, Literaturverweise). Das muss kein Essay sein, aber ein paar Sätze + Referenzen schaffen Vertrauen in die wissenschaftliche Fundierung.\nKontakt & Weiterführung: Ist angegeben, wie man Hilfe bekommt oder Fehler melden kann (Issue-Tracker, E-Mail)? Gibt es Hinweise für Beiträge (falls erwünscht) oder zumindest die Information, wer die Autor*innen sind?\nRechtliches & Zitation: Liegt die Lizenz bei und wird sie genannt? Sind Infos zum Zitieren der Software vorhanden (z. B. “Bitte zitieren Sie DOI XYZ”)? Das stellt sicher, dass die Software nachnutzbar und akademisch kreditiert wird.\nAktualität & Version: Entspricht die Dokumentation der aktuellen Softwareversion? (Check: Versionsnummern, Datumsangaben). Veraltete Doku kann schlimmer sein als keine – planen Sie also ein, die Doku mit jedem Release kurz zu überprüfen.\nKonsistenz & Stil: Wird ein einheitlicher Ton und Stil durchgehalten? (z. B. durchgehende Verwendung gleicher Begriffe für Konzepte, Sprache entweder Deutsch oder Englisch einheitlich je nach Zielgruppe). Kleinliche Fehler (Tippfehler, kaputte Links) sind auszumerzen, da sie Nutzer abschrecken.\n\nDiese Checkliste kann vor einem “Release” der Software durchgegangen werden, ähnlich einem Review-Prozess (vgl. JOSS Review-Kriterien, die viele dieser Punkte abdecken). Sie hilft zu entscheiden, was noch dokumentiert werden muss und was eventuell weggelassen werden kann. Alles, was für die obigen Punkte nicht relevant ist, kann man tendenziell aus der Hauptdokumentation herauslassen.\n\n\nImplementierung aller Vorschläge als ready-to-use Repository\n\n\n\n\n\n\nTODO\n\n\n\n\nHier noch auf unsere Template-Repos verweisen.\nTemplate-Repos selbst ggf. automatisch auf Zenodo mit kleinem Erklärungstext veröffentlichen?",
- "crumbs": [
- "Home",
- "Serious",
- "Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
- ]
- },
- {
- "objectID": "Writing/documentation.html#fazit",
- "href": "Writing/documentation.html#fazit",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Fazit",
- "text": "Fazit\nDie hier präsentierten Anforderungen und Empfehlungen bieten einen Leitfaden für die Dokumentation von Forschungssoftware in den Digital Humanities. Sie sind darauf ausgerichtet, mit überschaubarem Aufwand maximale Nachvollziehbarkeit, Langlebigkeit und Wiederverwendbarkeit zu erreichen.\nIndem zentrale Inhalte (Ziele, Inputs/Outputs, Hintergrund, etc.) klar dokumentiert, ein nutzerfreundliches Format (README im Repo) gewählt, der Umfang fokussiert gehalten und hilfreiche Tools eingesetzt werden, kann die Dokumentation zur Stärke eines Projekts werden statt einem lästigen Anhängsel.\nSo schließt sich der Kreis zwischen guter Softwareentwicklung und guter Wissenschaft[10, Leitlinie 12]: Dokumentation ist das Bindeglied, das Code und Erkenntnis transparent verbindet. In der Praxis bedeutet dies zwar zusätzliche Arbeitsschritte, doch wie die Erfahrung zeigt, zahlen sich diese in Form von Zeiteinsparung bei Nutzern, höherer Zitierbarkeit und größerer Wirkung der Software aus. Mit diesem Anforderungskatalog sind Forschende gut gerüstet, um ihre Softwareprojekte dokumentationstechnisch auf ein solides Fundament zu stellen – trotz knapper Zeit und ohne Informatikabschluss. Denn am Ende gilt: Gut dokumentierte Forschungscode ist nachhaltige Forschung.",
- "crumbs": [
- "Home",
- "Serious",
- "Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
- ]
- },
- {
- "objectID": "Writing/documentation.html#tabellarische-übersicht-der-dokumentations-bestandteile",
- "href": "Writing/documentation.html#tabellarische-übersicht-der-dokumentations-bestandteile",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Tabellarische Übersicht der Dokumentations-Bestandteile",
- "text": "Tabellarische Übersicht der Dokumentations-Bestandteile\n\n\n\nEmpfohlene Dokumentationselemente, Inhalte und Umfang. Diese Übersicht kann als Vorlage dienen, welche Komponenten ein Dokumentationspaket enthalten sollte. Je nach Projekt können einige Elemente wegfallen oder kombiniert werden – entscheidend ist, dass die Kerninformationen (siehe oben) nicht fehlen.\n\n\n\n\n\n\n\n\nDokuelement\nInhalt/Purpose\nFormat/Ort\nUmfang\n\n\n\n\nREADME (Hauptdoku)\nZweck der Software; Kurzbeschreibung; Installationsanleitung; einfaches Nutzungsbeispiel; Lizenz- und Kontaktinfo\nMarkdown im Root des Repos (statisch versioniert)\n1–2 Seiten\n\n\nEingabe/Ausgabe-Guide\nBeschreibung der erwarteten Inputs (Datenformat, Parameter) und generierten Outputs (Dateien, Berichte) inkl. Beispielen\nTeil der README oder separate Datei (z.B. USAGE.md)\n1 Seite (mit Beispielen)\n\n\nWissenschaftlicher Hintergrund\nErläuterung der Methode, Theorie, Algorithmen; Verweise auf Literatur\nREADME-Abschnitt “Hintergrund” oder separate Doku (BACKGROUND.md)\n0.5–1 Seite (plus Referenzen)\n\n\nBekannte Limitationen\nAuflistung von Einschränkungen, Annahmen, bekannten Problemen; ggf. Workarounds\nREADME-Abschnitt “Limitations” oder FAQ.md\n0.5 Seite\n\n\nBeispiel-Workflow (Tutorial)\nSchritt-für-Schritt Anleitung mit einem realistischen Anwendungsfall (ggf. mit Code und Screenshot)\nJupyter Notebook (.ipynb) im Repo examples/ Ordner oder Markdown in docs/\n1–3 Seiten / entsprechend Zellen\n\n\nAPI-Referenz\nTechnische Dokumentation von Funktionen/Klassen für Entwickler*innen\nAutomatisch generiert aus Docstrings (z.B. Sphinx in docs/ Ordner, HTML/PDF Ausgabe)\nJe nach Codegröße (ggf. umfangreich)\n\n\nCONTRIBUTING\nAnleitung für Beitragswillige: Code Style, Workflow, Tests, Kontakt\nCONTRIBUTING.md im Repo\n0.5–1 Seite\n\n\nLICENSE / CITATION\nRechtliche Infos (Lizenztext); Zitationsleitfaden (Bevorzugte Zitierweise, DOI)\nJeweils eigene Datei im Repo (Plain Text/Markdown)\nKurz (Standardtext bzw. Referenz)\n\n\nRelease-Information\nVersionshinweise, Änderungsprotokoll (Changelog)\nCHANGELOG.md oder Releases auf GitHub\nfortlaufend pro Version (Stichpunkte)",
- "crumbs": [
- "Home",
- "Serious",
- "Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
- ]
- },
- {
- "objectID": "Writing/documentation.html#referenz-websitesservices",
- "href": "Writing/documentation.html#referenz-websitesservices",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Referenz Websites/Services",
- "text": "Referenz Websites/Services\n\n\nGitHub: Seite mit sehr vielen Open-Source-Projekten, die git verwenden. Gehört zu Microsoft\nGitLab: Open-Source-Lösung für selbst gehostete Projektverwaltung (git, issue-tracking, …). Community (kostenfrei; limitierte features) oder Enterprise-Linzenz\nJOSS: The Journal of Open Source Software is a developer friendly, open access journal for research software packages.\nJORS: The Journal of Open Research Software features peer reviewed Software Metapapers describing research software with high reuse potential.",
- "crumbs": [
- "Home",
- "Serious",
- "Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
- ]
- },
- {
- "objectID": "Writing/documentation.html#referenz-software",
- "href": "Writing/documentation.html#referenz-software",
- "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
- "section": "Referenz Software",
- "text": "Referenz Software\n\n\nargparse: Der Argument-Parser der Python-Standardbibliothek\nDoxygen: Generator um aus C/C++ Docstrings eine Dokumentation zu generieren\ngit: Versionskontrollsystem\ngraphviz: Textuelle darstellung von Graphen; Standard-Unix-Tool; Auf vielen Systemen verfügbar und rendert zu pdf/svg\nJavadoc: Generator um aus Java Docstrings eine Dokumentation zu generieren\nMarkdown: Mittlerweile DER Standard bei plaintext-Dokumenten\nmermaid.js: Sprache für Diagramme; kann automatisiert (z.b. durch pandoc, javascript im HTML, …) in Bilder gewandelt werden\nMkDocs: Sehr einfacher und minimalistischer Generator für statische Websites aus Markdown\npandoc: DER Konverter für Dokumente. Kann sehr viel in Markdown wandeln und hieraus HTML/PDF u.ä. erstellen\npdoc: Generator um aus Python Docstrings eine Dokumentation zu generieren\npylint: Linting-Tool für Python. Formatiert Code und weist auf Probleme (z.b. fehlende Dokumentation) hin.\nRoxygen: Generator um aus R Docstrings eine Dokumentation zu generieren\nrst: Alternative zu Markdown.\nSphinx: Mächtiges Dokumentations-Generierungs-Werkzeug, welches hinter readthedocs.com steht.",
- "crumbs": [
- "Home",
- "Serious",
- "Writing",
- "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ "Field Report: Coding in the Age of AI with Cursor"
]
},
{
@@ -390,90 +325,6 @@
"Keyboard-Layout"
]
},
- {
- "objectID": "Coding/Haskell/Advantages.html",
- "href": "Coding/Haskell/Advantages.html",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "Gründe Haskell zu nutzen und wo Vorteile liegen.\n\n\n\nThe Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness\n\n\n\n\n\nPapers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers\n\n\n\n\n\nTom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)\n\n\n\n\n\nFunctional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett\n\n\n\n\n\nHaskell fast and hard\nCounterexamples for Typeclasses",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Advantages.html#talks",
- "href": "Coding/Haskell/Advantages.html#talks",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "The Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Advantages.html#bücherpaper",
- "href": "Coding/Haskell/Advantages.html#bücherpaper",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "Papers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Advantages.html#funny-talks",
- "href": "Coding/Haskell/Advantages.html#funny-talks",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "Tom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Advantages.html#unsortedunseen",
- "href": "Coding/Haskell/Advantages.html#unsortedunseen",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "Functional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Advantages.html#tutorials",
- "href": "Coding/Haskell/Advantages.html#tutorials",
- "title": "Talks und Posts zu Haskell",
- "section": "",
- "text": "Haskell fast and hard\nCounterexamples for Typeclasses",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Talks und Posts zu Haskell"
- ]
- },
{
"objectID": "Coding/Haskell/FFPiH.html",
"href": "Coding/Haskell/FFPiH.html",
@@ -516,262 +367,6 @@
"Fortgeschrittene funktionale Programmierung in Haskell"
]
},
- {
- "objectID": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
- "href": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
- "title": "Webapp-Example: MyService/Types.hs",
- "section": "",
- "text": "Anleitung siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n\nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n\n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n\ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n\n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n\n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n\n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n\n\ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Webapp-Development in Haskell",
- "Webapp-Example: MyService/Types.hs"
- ]
- },
- {
- "objectID": "Coding/Haskell/Code Snippets/Morphisms.html",
- "href": "Coding/Haskell/Code Snippets/Morphisms.html",
- "title": "*-Morpisms",
- "section": "",
- "text": "Note\n\n\n\nBackup eines Blogposts eines Kommilitonen\n\n\nThis weekend I spend some time on Morphisms.\nKnowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.\n\nFor those who need a refresher: MergeSort works by creating a balanced binary tree from the input list and directly collapsing it back into itself while treating the children as sorted lists and merging these with an O(n) algorithm.\n\nFirst the usual prelude:\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE TypeFamilies #-}\n\nimport Data.Functor.Foldable\nimport Data.List (splitAt, unfoldr)\n\nWe will use a binary tree like this. Note that there is no explicit recursion used, but NodeF has two holes. These will eventually filled later.\ndata TreeF c f = EmptyF | LeafF c | NodeF f f\n deriving (Eq, Show, Functor)\n\nAside: We could use this as a normal binary tree by wrapping it in Fix: type Tree a = Fix (TreeF a) But this would require us to write our tree like Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r'))) which would get tedious fast. Luckily Edward build a much better way to do this into recursion-schemes. I will touch on this later.\n\nWithout further ado we start to write a Coalgebra, which in my book is just a scary name for “function that is used to construct datastructures”.\nunflatten :: [a] -> TreeF a [a]\nunflatten ( []) = EmptyF\nunflatten (x:[]) = LeafF x\nunflatten ( xs) = NodeF l r where (l,r) = splitAt (length xs `div` 2) xs\nFrom the type signature it’s immediately obvious, that we take a list of ’a’s and use it to create a part of our tree.\nThe nice thing is that due to the fact that we haven’t commited to a type in our tree nodes we can just put lists in there.\n\nAside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:\nexample1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))\n\nOn to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.\nThe function mergeList is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the ordlist package if I were to implement this for real.\nAgain we see that we can just construct our sorted output list from a TreeF that apparently contains just lists.\nflatten :: Ord a => TreeF a [a] -> [a]\nflatten EmptyF = []\nflatten (LeafF c) = [c]\nflatten (NodeF l r) = mergeLists l r\n\nAside: We could use a Coalgebra to deconstruct trees:\nexample2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]\n\nNow we just combine the Coalgebra and the Algebra with one from the functions from Edwards recursion-schemes library:\nmergeSort :: Ord a => [a] -> [a]\nmergeSort = hylo flatten unflatten\n\nexample3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9]\n\nWhat have we gained?\nWe have implemented a MergeSort variant in 9 lines of code, not counting the mergeLists function below. Not bad, but this implementation is not much longer.\nOn the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.\nMy guess is that, as soon as the algortihms get more complex, this will really make a difference.\n\nAt this point I wasn’t sure if this was useful or remotely applicable. Telling someone “I spend a whole weekend learning about Hylomorphism” isn’t something the cool developer kids do.\nIt appeared to me that maybe I should have a look at the Core to see what the compiler finally comes up with (edited for brevity):\n mergeSort :: [Integer] -> [Integer]\n mergeSort =\n \\ (x :: [Integer]) ->\n case x of wild {\n [] -> [];\n : x1 ds ->\n case ds of _ {\n [] -> : x1 ([]);\n : ipv ipv1 ->\n unfoldr\n lvl9\n (let {\n p :: ([Integer], [Integer])\n p =\n case $wlenAcc wild 0 of ww { __DEFAULT ->\n case divInt# ww 2 of ww4 { __DEFAULT ->\n case tagToEnum# (<# ww4 0) of _ {\n False ->\n case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -> (ww2, ww3) };\n True -> ([], wild)\n }\n }\n } } in\n (case p of _ { (x2, ds1) -> mergeSort x2 },\n case p of _ { (ds1, y) -> mergeSort y }))\n }\n }\n end Rec }\nWhile I am not really competent in reading Core and this is actually the first time I bothered to try, it is immediately obvious that there is no trace of any intermediate tree structure.\nThis is when it struck me. I was dazzled and amazed. And am still. Although we are writing our algorithm as if we are working on a real tree structure the library and the compiler are able to just remove the whole intermediate step.\n\nAftermath:\nIn the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the recursion-schemes library.\nWe are able to create a ‘normal’ version of our tree from above:\ndata Tree c = Empty | Leaf c | Node (Tree c) (Tree c)\n deriving (Eq, Show)\nBut we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:\ntype instance Base (Tree c) = (TreeF c)\n\ninstance Unfoldable (Tree c) where\n embed EmptyF = Empty\n embed (LeafF c) = Leaf c\n embed (NodeF l r) = Node l r\n\ninstance Foldable (Tree c) where\n project Empty = EmptyF\n project (Leaf c) = LeafF c\n project (Node l r) = NodeF l r\nWithout going into detail by doing this we establish a relationship between Tree and TreeF and teach the compiler how to translate between these types.\nNow we can use our Alebra on our non functor type:\nexample4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == \"lr\"\nThe great thing about this is that, looking at the Core output again, there is no traces of the TreeF structure to be found. As far as I can tell, the algorithm is working directly on our Tree type.\n\nLiterature:\n\nUnderstanding F-Algebras\nRecursion Schemes by Example\nRecursion Schemes: A Field Guide\nThis StackOverflow question\n\n\nAppendix:\nmergeLists :: Ord a => [a] -> [a] -> [a]\nmergeLists = curry $ unfoldr c where\n c ([], []) = Nothing\n c ([], y:ys) = Just (y, ([], ys))\n c (x:xs, []) = Just (x, (xs, []))\n c (x:xs, y:ys) | x <= y = Just (x, (xs, y:ys))\n | x > y = Just (y, (x:xs, ys))",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Code Snippets",
- "*-Morpisms"
- ]
- },
- {
- "objectID": "About/index.html",
- "href": "About/index.html",
- "title": "About me",
- "section": "",
- "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
- "crumbs": [
- "Home",
- "Info",
- "About me"
- ]
- },
- {
- "objectID": "About/index.html#work",
- "href": "About/index.html#work",
- "title": "About me",
- "section": "",
- "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
- "crumbs": [
- "Home",
- "Info",
- "About me"
- ]
- },
- {
- "objectID": "About/index.html#education",
- "href": "About/index.html#education",
- "title": "About me",
- "section": "Education",
- "text": "Education\n\nBachelor “Kognitive Informatik” (Cognitive Informatics) in Bielefeld 2010-2014\nMaster “Naturwissenschaftliche Informatik” (Informatics in the natural sciences) 2014-2018\n\n\nExtraordinary grades (Excerpt of my Transcript)\nNote: Scale of grades in Germany is 1.0 to 4.0 with 1.0 being best, 4.0 being passing grade, 5.0 being failed grade\n\n1.0 in Modern Data Analysis\n\nMaster course on data-analysis (time-series, core-vector-machines, gaussian processes, …)\n\n1.0 in Computergraphics\n\nRaytracing, Modern OpenGL\n\n1.3 in Computer-Animation\n\nDual-Quarternion-Skinning, Character-Animation, FACS-Poses, etc.\n\n1.3 in GPU-Computing (CUDA)\n\noriginally a 1.7 by timing (task was de-mosaicing on images, grade was measured in ms, whereby 400ms equated to 4.0 and 100ms equated to 1.0), but because my deep knowledge was visible in the code i was given a 1.3 after oral presentation.\n\n1.0 in Parallel Algorithms and Data-Structures\nEthical Hacking\n\nReverse Engineering with IDApro",
- "crumbs": [
- "Home",
- "Info",
- "About me"
- ]
- },
- {
- "objectID": "About/index.html#further-information",
- "href": "About/index.html#further-information",
- "title": "About me",
- "section": "Further information",
- "text": "Further information\n\nMore details on my work-experience\nMore details of my coding\nMore details of things i did beside studying at University",
- "crumbs": [
- "Home",
- "Info",
- "About me"
- ]
- },
- {
- "objectID": "About/Work.html",
- "href": "About/Work.html",
- "title": "Work-Experience",
- "section": "",
- "text": "Work-Experience\n\nMar. 2023 to Sep. 2023:\n\nDeveloper for 2Lambda.co. Role migrated from just coding stuff to architecting and rewriting the whole software from the ground up using a small modular approach instead of the shaky one-off systems in place.\nWas later a “nanny for everything”.\nDid a lot of work to have self-documenting code (i.e. generate documentation from the actual values used in the program, not some comments that always get out of date)\nSetting up a knowledge-base (Zettelkasten-approach) to track experiments and hyperlink them to the documentation generated above (and due to Zettelkasten you then get “this thing was used in Experiments a, b and c” automatically\nTechnologies used:\n\nClojure\n\nComplete application was written in Clojure\nNever touched that language before March - got up to speed in just 2 days, poked the expert on the team detailed questions about the runtime-system after 1 month (like inlining-behavior, allocation-things, etc.)\n\nEmanote\n\nautogenerated & linked documentation of internal modules\nintegrated with manual written tutorials/notes\ncrosslinking documentation of experiments with documentation of modules\n\nWeb of knowledge\nbidirectional discovery of things tried/done in the past to optimize finding of new strategies (meta-optimizing the decisions on what to optimize/try)\n\n\nInfrastructure\n\nOrganized and co-administrated the 4 Root-Servers we had\nSet up Kubernetes, Nexus, Docker, Nginx, letsencrypt-certs, dns-entries, etc..\n\n\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\nExtraction/Classification of sentences from JobAds (Requirements, Benefits, Tasks, …)\nExtraction of Information from JobAds (Location of company, Location of workplay, contact-details, application-procedure, etc.) including geocoding of those information (backed by OpenStreetMap)\nEmbedding of JobAds into a meaningful space (i.e. “get me similar ads. btw. i dislike ad a, b, c”).\nAnalyse & predict search-queries of users on the webpage and offer likely but distinct queries (i.e. similar when typo or complete different words (synonyms, hyponyms, etc.))\n\nTechnologies used:\n\nHaskell (currently GHC 8.6, soon GHC 8.8)\n\nstack + stackage-lts\nfixplate (recursion-schemes-implementation)\nmany usual technologies like lens, http-simple, mtl, ..\ngolden-testing via tasty\nseveral inhouse-developments:\n\ntemplating based on text-replacement via generics (fieldname in Template-Type == variable replaced in template)\nactiveMQ/Kibana-bridge for logging via hs-stomp\ngeneric internal logging-framework\n\n\nPython\n\ntensorflow\npytorch\nsklearn\nnltk\n\n\n\n2013-2018:\n\nseveral jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “[[FFPiH|Intermediate Functional Programming in Haskell]]”\n\nOriginally developed as student-project in cooperation with Jonas Betzendahl\nFirst held in Summer 2015\nDue to high demand held again in Summer 2016 and 2017\nWas awarded Lecturer-Award “silver Chalk” in 2016\n\nFirst time that this award was given to students\nMany lecturers at our faculty never get any teaching-award until retirement\n\n\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader “Workgroup Computer Graphics”) using Pandoc & reveal.js\n\nFramework: https://github.com/mbotsch/revealSlides\nExample: https://github.com/mbotsch/eLearning\nPandoc-Filters: https://github.com/mbotsch/pandoc-slide-filter",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Work-Experience"
- ]
- },
- {
- "objectID": "About/Experience.html",
- "href": "About/Experience.html",
- "title": "Highlights of my experiences in the programming world",
- "section": "",
- "text": "(as far as NDA and other things allow it)\n\n\n\nLearning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell\n\n\n\n\n\nI also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Highlights of my experiences in the programming world"
- ]
- },
- {
- "objectID": "About/Experience.html#haskell-enthusiast",
- "href": "About/Experience.html#haskell-enthusiast",
- "title": "Highlights of my experiences in the programming world",
- "section": "",
- "text": "Learning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Highlights of my experiences in the programming world"
- ]
- },
- {
- "objectID": "About/Experience.html#gitea",
- "href": "About/Experience.html#gitea",
- "title": "Highlights of my experiences in the programming world",
- "section": "",
- "text": "I also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Highlights of my experiences in the programming world"
- ]
- },
- {
- "objectID": "About/Extracurricular.html",
- "href": "About/Extracurricular.html",
- "title": "Studium generale / University-Life",
- "section": "",
- "text": "(What I did at university besides studying :sunglasses: )\n\n\n\nStudent Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs\n\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Studium generale / University-Life"
- ]
- },
- {
- "objectID": "About/Extracurricular.html#committees-student-body",
- "href": "About/Extracurricular.html#committees-student-body",
- "title": "Studium generale / University-Life",
- "section": "",
- "text": "Student Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Studium generale / University-Life"
- ]
- },
- {
- "objectID": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
- "href": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
- "title": "Studium generale / University-Life",
- "section": "",
- "text": "Fortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
- "crumbs": [
- "Home",
- "Info",
- "About me",
- "Studium generale / University-Life"
- ]
- },
- {
- "objectID": "Coding/Haskell/Code Snippets/Monoid.html",
- "href": "Coding/Haskell/Code Snippets/Monoid.html",
- "title": "Monoid? Da war doch was…",
- "section": "",
- "text": "Stellen wir uns vor, dass wir eine Funktion schreiben, die einen String bekommt (mehrere Lines mit ACSII-Text) und dieses Wort-für-Wort rückwärts ausgeben soll. Das ist ein einfacher Einzeiler:\nWas passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?\nGehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:\nWir bekommen als eine Liste der Argumente. Wir wollen nur das erste. Also machen wir head getArgs. Allerdings fliegt uns dann ein Fehler. head sieht nämlich so aus:\nIrgendwie müssen wird as in das IO bekommen. Hierzu gibt es fmap. Somit ist\nEin inline-Alias (um die Funktion links und das Argument rechts zu schreiben und sich ne Menge Klammern zu sparen) ist <$>. Somit ist schlussendlich der Inhalt der Datei aus dem ersten Argument (lazy) in ls.\nEine andere Möglichkeit sich das (in diesem Fall) zu merken, bzw. drauf zu kommen ist, dass [] AUCH ein Funktor (sogar eine Monade) ist. Man könnte das also auch so schreiben:\nfmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.\nAlternatives (ausführliches) Beispiel am Ende.\nWenn wir uns die Signatur ansehen, dann haben wir nun\nreadFile will aber nun ein String haben. Man kann nun\nkann man auch “inline” mit =<< die Sachen “auspacken”.\nDie 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein\nwas uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:\nWenn wir uns die Signatur ansehen:\nDas mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:\nDa wir am Ende in der IO-Monade landen müssen wir das auf unsere Zeilen mit mapM statt map anwenden. Dies sorgt auch dafür, dass die Liste der reihe nach durchgegangen wird. mapM mit unserer Funktion schaut dann so aus:\neek! Das [IO ()] sieht ekelig aus. Wir haben eine Liste von IO-gar nichts. Das können wir eigentlich entsorgen. Da wir innerhalb der main-Funktion in einer IO-Monade sind, wollen wir IO () anstatt [IO ()] zurück haben.\nWenn wir uns jetzt erinnern, dass [] auch nur eine Monade ist und dass jede Monade ein Monoid ist, dann ist die Lösung einfach. Monoide haben eine “append”-funktion (mappend oder (<>) genannt). Wenn wir “nichts” an “nichts” anhängen, dann erhalten wir …. Trommelwirbel “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:\nWas genau die gewünschte Faltung macht. Wir müssen nun wieder fmap nehmen, da wir die Liste selbst falten wollen - und nicht map, welches auf den IO () innerhalb der Liste arbeiten würde. Durch die Faltung fällt die Liste nun auf IO () zusammen.\nViel Voodoo in wenig Code, aber wenn man sich dran gewöhnt hat, sind Monaden in Monaden auch nicht schlimm. Man muss sich immer nur richtig “rein” fmap’en.\nKleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch\nschreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Code Snippets",
- "Monoid? Da war doch was..."
- ]
- },
- {
- "objectID": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
- "href": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
- "title": "Monoid? Da war doch was…",
- "section": "Alternatives fmap-Beispiel",
- "text": "Alternatives fmap-Beispiel\nNehmen wir als alternatives Beispiel mal an:\na :: IO Maybe State t\nUm Funktionen vom Typ\nf :: IO a -> IO a\nf a -- valide\nzu nehmen, brauchen wir nichts machen. Bei\nf' :: Maybe a -> Maybe a\nbrauchen wir 1 fmap, also ein\nf' a -- error\nf' <$> a\num eine Funktion\nf'' :: State t -> State t\nzu benutzen folglich:\nf'' a -- error\nf'' <$> a -- error\nfmap f'' <$> a",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Code Snippets",
- "Monoid? Da war doch was..."
- ]
- },
- {
- "objectID": "Coding/Haskell/Webapp-Example/Main.hs.html",
- "href": "Coding/Haskell/Webapp-Example/Main.hs.html",
- "title": "Webapp-Example: Main.hs",
- "section": "",
- "text": "Wie man das verwendet, siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Webapp-Development in Haskell",
- "Webapp-Example: Main.hs"
- ]
- },
- {
- "objectID": "Coding/Haskell/Webapp-Example/index.html",
- "href": "Coding/Haskell/Webapp-Example/index.html",
- "title": "Webapp-Development in Haskell",
- "section": "",
- "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Webapp-Development in Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
- "href": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
- "title": "Webapp-Development in Haskell",
- "section": "",
- "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Webapp-Development in Haskell"
- ]
- },
- {
- "objectID": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
- "href": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
- "title": "Webapp-Development in Haskell",
- "section": "Startprojekt in Haskell",
- "text": "Startprojekt in Haskell\n\nErstellen eines neuen Projektes\nZunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:\nstack new myservice\nDies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der stack.yaml (resolver auf unserer setzen; aktuell: lts-17.4) fügen wir am Ende der Datei\nallow-newer: true\nghc-options:\n \"$locals\": -fwrite-ide-info\nein. Anschließend organisieren™ wir uns noch eine gute .gitignore und initialisieren das git mittels git init; git add .; git commit -m \"initial scaffold\"\n\n\nGenerierung der API\nDa die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.\nInitial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die api-doc.yml hinein kopieren und folgendes ausführen:\nopenapi-generator generate -g haskell -o . -i api-doc.yml\nDieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der api-doc sollte vom Namen des Services (oben myservice) abweichen - entweder in Casing oder im Namen direkt. Suffixe wie API schneidet der Generator hier leider ab. (Wieso das ganze? Es entstehen nachher 2 libraries, foo & fooAPI. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).\ndanach: wie gewohnt git init; git add .; git commit -m \"initial\". Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: myserviceAPI - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.\n\nWieder zurück im Haskell-Service\nIn unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis libs (Konvention) und machen ein git submodule add <repository-url> libs/myserviceAPI\nGit hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.\nAnschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die stack.yaml an, indem wir das Verzeichnis unter packages hinzufügen.\npackages:\n - .\n - libs/myserviceAPI # <<\nNun können wir in der package.yaml (oder myservice.cabal, falls kein hpack verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in libs/myserviceAPI).\n\n\n\nEinbinden anderer Microservices\nFunktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). stack.yaml editieren und zu den packages hinzufügen:\npackages:\n - .\n - libs/myserviceAPI\n - libs/myCoolMLServiceAPI\nin der package.yaml (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.\n\n\nEntfernen von anderen Technologien/Microservices\nIn git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der GitHub-Antwort:\n## Remove the submodule entry from .git/config\ngit submodule deinit -f path/to/submodule\n\n## Remove the submodule directory from the superproject's .git/modules directory\nrm-rf .git/modules/path/to/submodule\n\n## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule\ngit rm-f path/to/submodule\nFalls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.\n\n\nWoher weiss ich, was wo liegt? Dokumentation? Halloo??\nKeine Panik. Ein stack haddock --open hilft da. Das generiert die Dokumentation für alle in der package.yaml (oder cabal-file) eingetragenen dependencies inkl. aller upstream-dependencies. Man bekommt also eine komplette lokale Dokumentation von allem. Geöffnet wird dann die Paket-Startseite inkl. der direkten dependencies:\nEs gibt 2 wichtige Pfade im Browser:\n\n...../all/index.html - hier sind alle Pakete aufgeführt\n...../index.html - hier sind nur die direkten dependencies aufgeführt.\n\nWenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit python3 geht das z.b. einfach über:\ncd $(stack path --local-doc-root)\npython3 -m SimpleHTTPServer 8000\nfirefox \"http://localhost:8000\"\n\n\nImplementation des Services und Start\n\nLoader/Bootstrapper\nGenerelles Vorgehen:\n\nin app/Main.hs: Hier ist quasi immer nur eine Zeile drin: main = myServiceMain\nGrund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.\nin src/MyService.hs: myServiceMain :: IO () definieren\n\nFür die Main kann man prinzipiell eine Main andere Services copy/pasten. Im folgenden eine Annotierte main-Funktion - zu den einzelnen Voraussetzungen kommen wir im Anschluss.\nMain.hs anzeigen\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"\n{{< dend >}}\n\n\nWeitere Instanzen und Definitionen, die der Generator (noch) nicht macht\nIn der Myservice.Types werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:\n\nEnvy\n\nLaden von $ENV_VAR in Datentypen\nDefinitionen für Default-Settings\n\nServerConfig\n\nDefinition der Server-Konfiguration & Benennung der Environment-Variablen\n\nExtraTypes\n\nggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)\n\nOut/BSON-Instanzen\n\nDer API-Generator generiert nur wenige Instanzen automatisch (z.B. aeson), daher werden hier die fehlenden definiert.\nBSON: Kommunikation mit MongoDB\nOut: pretty-printing im Log\n\nNur nötig, wenn man pretty-printing via Out statt über Generics wie z.b. pretty-generic oder die automatische Show-Instanz via prerryShow macht.\n\n\n\nTypes.hs anzeigen\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n \nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n \n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n \ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n \n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n \n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n \n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n \n \ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).\n{{< dend >}}\n\n\nWas noch zu tun ist\nDen Service implementieren. Einfach ein neues Modul aufmachen (z.B. MyService.Handler oder MyService.DieserEndpunktbereich/MyService.JenerEndpunktbereich) und dort die Funktion implementieren, die man in der Main.hs benutzt hat. In dem Handler habt ihr dann keinen Stress mehr mit Validierung, networking, logging, etc. pp. weil alles in der Main abgehandelt wurde und ihr nur noch den “Happy-Case” implementieren müsst. Beispiel für unseren Handler oben:\nmyApiEndpointV1Post :: MonadIO m => ServerConfig -> (ClientEnv,ClientEnv) -> TQueue BS.ByteString -> ([LogItem] -> IO ()) -> Request -> m Response\nmyApiEndpointV1Post sc calls amqPost log req = do\n liftIO . log $ [Info $ \"recieved \"<>pretty req] -- input-logging\n liftIO . atomically . writeTQueue . LBS.toStrict $ \"{\\\"hey Kibana, i recieved:\\\"\" <> A.encode (pretty req) <> \"}\" -- log in activeMQ/Kibana\n\n\n --- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)\n let ret = Response 1337 Nothing -- dummy-response ;)\n -- gegeben wir haben eine gültige mongodb-pipe;\n -- mehr logik will ich in die Beispiele nicht packen.\n -- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.\n liftIO . access pipe master \"DatabaseName\" $ do\n ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (return ()) (liftIO . printLog . pure . Error $ \"MongoDB: Login failed.\")\n save \"DatabaseCollection\" [\"_id\" =: 1337, \"entry\" =: ret] -- selbe id wie oben ;)\n return ret\nDiese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.\n\nLogging in die Dateien/stdout - je nach Konfiguration\nLogging von Statistiken in Kibana\nSpeichern der Antwort in der MongoDB\nGenerieren einer Serverantwort und ausliefern dieser über die Schnittstelle\n\n\n\nTipps & Tricks\n\nDateien, die statisch ausgeliefert werden sollen\nHierzu erstellt man ein Verzeichnis static/ (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine index.html rein, erscheint die, wenn man den Service ansurft.\n\n\nWie bekomme ich diese fancy Preview hin?\nDer Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die api-doc.yml um diese Ansicht zu erzeugen. Daher empfiehlt sich hier ein angepasster Fork davon indem die Pfade in der index.html korrigiert sind. Am einfachsten (und von den meisten services so benutzt): In meiner Implementation liegt dann nach dem starten auf http://localhost:PORT/ui/ und kann direkt dort getestet werden.\n\n\nWie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?\nstack build --file-watch --ghc-options '-freverse-errors -W -Wall -Wcompat' --interleaved-output\nWas tut das?\n\n--file-watch: automatisches (minimales) kompilieren bei dateiänderungen\n--ghc-options\n\n-freverse-errors: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )\n-W: Warnungen an\n-Wall: Alle sinnvollen Warnungen an (im gegensatz zu -Weverything, was WIRKLICH alles ist )\n-Wcompat: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten\n\n--interleaved-output: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.\n\nUm pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): {-# OPTIONS_GHC -Wno-whatsoever #-} als pragma in die Datei.\nIdealerweise sollte das Projekt keine Warnungen erzeugen.\n\n\n\n\nDeployment\nAls Beispiel sei hier ein einfaches Docker-Build mit Jenkins-CI gezeigt, weil ich das aus Gründen rumliegen hatte. Kann man analog in fast alle anderen CI übersetzen.\n\nDocker\nDie angehängten Scripte gehen von einer Standard-Einrichtung aus (statische Sachen in static, 2-3 händische Anpassungen auf das eigene Projekt nach auspacken). Nachher liegt dann auch unter static/version die gebaute Versionsnummer & kann abgerufen werden. In der Dockerfile.release und der Jenkinsfile müssen noch Anpassungen gemacht werden. Konkret:\n\nin der Dockerfile.release: alle <<<HIER>>>-Stellen sinnvoll befüllen\nin der Jenkinsfile die defs für “servicename” und “servicebinary” ausfüllen. Binary ist das, was bei stack exec aufgerufen wird; name ist der Image-Name für das docker-repository.\n\n\n\nJenkins\nÄnderungen die dann noch gemacht werden müssen:\n\ngit-repository URL anpassen\nEnvironment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)\n\nWenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens servicename:version.\n\n\n\nOMG! Ich muss meine API ändern. Was mache ich nun?\n\napi-doc.yml bearbeiten, wie gewünscht\nmittels generator die Api & submodule neu generieren\nggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)\nAlle Compilerfehler + Warnungen in der eigentlichen Applikation fixen\nIf it comipilez, ship it! (Besser nicht :grin:)",
- "crumbs": [
- "Home",
- "Serious",
- "Coding",
- "Haskell",
- "Webapp-Development in Haskell"
- ]
- },
{
"objectID": "Coding/Haskell/Lenses.html",
"href": "Coding/Haskell/Lenses.html",
@@ -1024,6 +619,346 @@
"Lenses"
]
},
+ {
+ "objectID": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
+ "href": "Coding/Haskell/Webapp-Example/MyService_Types.hs.html",
+ "title": "Webapp-Example: MyService/Types.hs",
+ "section": "",
+ "text": "Anleitung siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n\nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n\n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n\ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n\n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n\n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n\n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n\n\ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Webapp-Development in Haskell",
+ "Webapp-Example: MyService/Types.hs"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Code Snippets/Morphisms.html",
+ "href": "Coding/Haskell/Code Snippets/Morphisms.html",
+ "title": "*-Morpisms",
+ "section": "",
+ "text": "Note\n\n\n\nBackup eines Blogposts eines Kommilitonen\n\n\nThis weekend I spend some time on Morphisms.\nKnowing that this might sound daunting to many dabbling Haskellers (like I am), I decided to write a real short MergeSort hylomorphism quickstarter.\n\nFor those who need a refresher: MergeSort works by creating a balanced binary tree from the input list and directly collapsing it back into itself while treating the children as sorted lists and merging these with an O(n) algorithm.\n\nFirst the usual prelude:\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE TypeFamilies #-}\n\nimport Data.Functor.Foldable\nimport Data.List (splitAt, unfoldr)\n\nWe will use a binary tree like this. Note that there is no explicit recursion used, but NodeF has two holes. These will eventually filled later.\ndata TreeF c f = EmptyF | LeafF c | NodeF f f\n deriving (Eq, Show, Functor)\n\nAside: We could use this as a normal binary tree by wrapping it in Fix: type Tree a = Fix (TreeF a) But this would require us to write our tree like Fix (NodeF (Fix (LeafF 'l')) (Fix (LeafF 'r'))) which would get tedious fast. Luckily Edward build a much better way to do this into recursion-schemes. I will touch on this later.\n\nWithout further ado we start to write a Coalgebra, which in my book is just a scary name for “function that is used to construct datastructures”.\nunflatten :: [a] -> TreeF a [a]\nunflatten ( []) = EmptyF\nunflatten (x:[]) = LeafF x\nunflatten ( xs) = NodeF l r where (l,r) = splitAt (length xs `div` 2) xs\nFrom the type signature it’s immediately obvious, that we take a list of ’a’s and use it to create a part of our tree.\nThe nice thing is that due to the fact that we haven’t commited to a type in our tree nodes we can just put lists in there.\n\nAside: At this point we could use this Coalgebra to construct (unsorted) binary trees from lists:\nexample1 = ana unflatten [1,3] == Fix (NodeF (Fix (LeafF 1)) (Fix (LeafF 3)))\n\nOn to our sorting, tree-collapsing Algebra. Which again is just a creepy word for “function that is used to deconstruct datastructures”.\nThe function mergeList is defined below and just merges two sorted lists into one sorted list in O(n), I would probably take this from the ordlist package if I were to implement this for real.\nAgain we see that we can just construct our sorted output list from a TreeF that apparently contains just lists.\nflatten :: Ord a => TreeF a [a] -> [a]\nflatten EmptyF = []\nflatten (LeafF c) = [c]\nflatten (NodeF l r) = mergeLists l r\n\nAside: We could use a Coalgebra to deconstruct trees:\nexample2 = cata flatten (Fix (NodeF (Fix (LeafF 3)) (Fix (LeafF 1)))) == [1,3]\n\nNow we just combine the Coalgebra and the Algebra with one from the functions from Edwards recursion-schemes library:\nmergeSort :: Ord a => [a] -> [a]\nmergeSort = hylo flatten unflatten\n\nexample3 = mergeSort [5,2,7,9,1,4] == [1,2,4,5,7,9]\n\nWhat have we gained?\nWe have implemented a MergeSort variant in 9 lines of code, not counting the mergeLists function below. Not bad, but this implementation is not much longer.\nOn the other hand the morphism based implementation cleanly describes what happens during construction and deconstruction of our intermediate structure.\nMy guess is that, as soon as the algortihms get more complex, this will really make a difference.\n\nAt this point I wasn’t sure if this was useful or remotely applicable. Telling someone “I spend a whole weekend learning about Hylomorphism” isn’t something the cool developer kids do.\nIt appeared to me that maybe I should have a look at the Core to see what the compiler finally comes up with (edited for brevity):\n mergeSort :: [Integer] -> [Integer]\n mergeSort =\n \\ (x :: [Integer]) ->\n case x of wild {\n [] -> [];\n : x1 ds ->\n case ds of _ {\n [] -> : x1 ([]);\n : ipv ipv1 ->\n unfoldr\n lvl9\n (let {\n p :: ([Integer], [Integer])\n p =\n case $wlenAcc wild 0 of ww { __DEFAULT ->\n case divInt# ww 2 of ww4 { __DEFAULT ->\n case tagToEnum# (<# ww4 0) of _ {\n False ->\n case $wsplitAt# ww4 wild of _ { (# ww2, ww3 #) -> (ww2, ww3) };\n True -> ([], wild)\n }\n }\n } } in\n (case p of _ { (x2, ds1) -> mergeSort x2 },\n case p of _ { (ds1, y) -> mergeSort y }))\n }\n }\n end Rec }\nWhile I am not really competent in reading Core and this is actually the first time I bothered to try, it is immediately obvious that there is no trace of any intermediate tree structure.\nThis is when it struck me. I was dazzled and amazed. And am still. Although we are writing our algorithm as if we are working on a real tree structure the library and the compiler are able to just remove the whole intermediate step.\n\nAftermath:\nIn the beginning I promised a way to work on non-functor data structures. Actually that was how I began to work with the recursion-schemes library.\nWe are able to create a ‘normal’ version of our tree from above:\ndata Tree c = Empty | Leaf c | Node (Tree c) (Tree c)\n deriving (Eq, Show)\nBut we can not use this directly with our (Co-)Algebras. Luckily Edward build a little bit of type magic into the library:\ntype instance Base (Tree c) = (TreeF c)\n\ninstance Unfoldable (Tree c) where\n embed EmptyF = Empty\n embed (LeafF c) = Leaf c\n embed (NodeF l r) = Node l r\n\ninstance Foldable (Tree c) where\n project Empty = EmptyF\n project (Leaf c) = LeafF c\n project (Node l r) = NodeF l r\nWithout going into detail by doing this we establish a relationship between Tree and TreeF and teach the compiler how to translate between these types.\nNow we can use our Alebra on our non functor type:\nexample4 = cata flatten (Node (Leaf 'l') (Leaf 'r')) == \"lr\"\nThe great thing about this is that, looking at the Core output again, there is no traces of the TreeF structure to be found. As far as I can tell, the algorithm is working directly on our Tree type.\n\nLiterature:\n\nUnderstanding F-Algebras\nRecursion Schemes by Example\nRecursion Schemes: A Field Guide\nThis StackOverflow question\n\n\nAppendix:\nmergeLists :: Ord a => [a] -> [a] -> [a]\nmergeLists = curry $ unfoldr c where\n c ([], []) = Nothing\n c ([], y:ys) = Just (y, ([], ys))\n c (x:xs, []) = Just (x, (xs, []))\n c (x:xs, y:ys) | x <= y = Just (x, (xs, y:ys))\n | x > y = Just (y, (x:xs, ys))",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Code Snippets",
+ "*-Morpisms"
+ ]
+ },
+ {
+ "objectID": "About/index.html",
+ "href": "About/index.html",
+ "title": "About me",
+ "section": "",
+ "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me"
+ ]
+ },
+ {
+ "objectID": "About/index.html#work",
+ "href": "About/index.html#work",
+ "title": "About me",
+ "section": "",
+ "text": "March 2024 to September 2028:\n\nResearch-Software-Engineer at “Digital History” workgroup at HU Berlin\nPart of NFDI4Memory, Task Area 5 “Data Culture”\nPhD-ing on the side\n\nJanuary 2024 to February 2024:\n\nWorked for yarvis\nFirst fulltime-developer; Responsive Webapp with react\n\nMarch 2023 to September 2023:\n\nWorked for 2lambda\nSilicon Valley start-up trying to beat the stock-market with fancy ML-Models\nThat work kickstarted my employment at Red Queen UG where i continue doing consulting work for 2lambda while also moving into a more senior role of also building up our own team of specialists to work on different future projects.\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\n2013-2018 several jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “Intermediate Functional Programming in Haskell”\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader Workgroup Computer Graphics) using Pandoc & reveal.js",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me"
+ ]
+ },
+ {
+ "objectID": "About/index.html#education",
+ "href": "About/index.html#education",
+ "title": "About me",
+ "section": "Education",
+ "text": "Education\n\nBachelor “Kognitive Informatik” (Cognitive Informatics) in Bielefeld 2010-2014\nMaster “Naturwissenschaftliche Informatik” (Informatics in the natural sciences) 2014-2018\n\n\nExtraordinary grades (Excerpt of my Transcript)\nNote: Scale of grades in Germany is 1.0 to 4.0 with 1.0 being best, 4.0 being passing grade, 5.0 being failed grade\n\n1.0 in Modern Data Analysis\n\nMaster course on data-analysis (time-series, core-vector-machines, gaussian processes, …)\n\n1.0 in Computergraphics\n\nRaytracing, Modern OpenGL\n\n1.3 in Computer-Animation\n\nDual-Quarternion-Skinning, Character-Animation, FACS-Poses, etc.\n\n1.3 in GPU-Computing (CUDA)\n\noriginally a 1.7 by timing (task was de-mosaicing on images, grade was measured in ms, whereby 400ms equated to 4.0 and 100ms equated to 1.0), but because my deep knowledge was visible in the code i was given a 1.3 after oral presentation.\n\n1.0 in Parallel Algorithms and Data-Structures\nEthical Hacking\n\nReverse Engineering with IDApro",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me"
+ ]
+ },
+ {
+ "objectID": "About/index.html#further-information",
+ "href": "About/index.html#further-information",
+ "title": "About me",
+ "section": "Further information",
+ "text": "Further information\n\nMore details on my work-experience\nMore details of my coding\nMore details of things i did beside studying at University",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me"
+ ]
+ },
+ {
+ "objectID": "About/Work.html",
+ "href": "About/Work.html",
+ "title": "Work-Experience",
+ "section": "",
+ "text": "Work-Experience\n\nMar. 2023 to Sep. 2023:\n\nDeveloper for 2Lambda.co. Role migrated from just coding stuff to architecting and rewriting the whole software from the ground up using a small modular approach instead of the shaky one-off systems in place.\nWas later a “nanny for everything”.\nDid a lot of work to have self-documenting code (i.e. generate documentation from the actual values used in the program, not some comments that always get out of date)\nSetting up a knowledge-base (Zettelkasten-approach) to track experiments and hyperlink them to the documentation generated above (and due to Zettelkasten you then get “this thing was used in Experiments a, b and c” automatically\nTechnologies used:\n\nClojure\n\nComplete application was written in Clojure\nNever touched that language before March - got up to speed in just 2 days, poked the expert on the team detailed questions about the runtime-system after 1 month (like inlining-behavior, allocation-things, etc.)\n\nEmanote\n\nautogenerated & linked documentation of internal modules\nintegrated with manual written tutorials/notes\ncrosslinking documentation of experiments with documentation of modules\n\nWeb of knowledge\nbidirectional discovery of things tried/done in the past to optimize finding of new strategies (meta-optimizing the decisions on what to optimize/try)\n\n\nInfrastructure\n\nOrganized and co-administrated the 4 Root-Servers we had\nSet up Kubernetes, Nexus, Docker, Nginx, letsencrypt-certs, dns-entries, etc..\n\n\n\nOct. 2018 to Aug. 2021:\n\nML-Specialist at Jobware (Paderborn; german Job-Advertising-Platform)\n\nExtraction/Classification of sentences from JobAds (Requirements, Benefits, Tasks, …)\nExtraction of Information from JobAds (Location of company, Location of workplay, contact-details, application-procedure, etc.) including geocoding of those information (backed by OpenStreetMap)\nEmbedding of JobAds into a meaningful space (i.e. “get me similar ads. btw. i dislike ad a, b, c”).\nAnalyse & predict search-queries of users on the webpage and offer likely but distinct queries (i.e. similar when typo or complete different words (synonyms, hyponyms, etc.))\n\nTechnologies used:\n\nHaskell (currently GHC 8.6, soon GHC 8.8)\n\nstack + stackage-lts\nfixplate (recursion-schemes-implementation)\nmany usual technologies like lens, http-simple, mtl, ..\ngolden-testing via tasty\nseveral inhouse-developments:\n\ntemplating based on text-replacement via generics (fieldname in Template-Type == variable replaced in template)\nactiveMQ/Kibana-bridge for logging via hs-stomp\ngeneric internal logging-framework\n\n\nPython\n\ntensorflow\npytorch\nsklearn\nnltk\n\n\n\n2013-2018:\n\nseveral jobs at my University including\n\nWorked 6 Months in the Workgroup “Theoretical Computer Science” on migrating algorithms to CUDA\nTutor “Introduction to Machine Learning”\n\nWas awarded Tutoring-Award of the Faculty of Technology for excellent tutoring\n\nLecture “[[FFPiH|Intermediate Functional Programming in Haskell]]”\n\nOriginally developed as student-project in cooperation with Jonas Betzendahl\nFirst held in Summer 2015\nDue to high demand held again in Summer 2016 and 2017\nWas awarded Lecturer-Award “silver Chalk” in 2016\n\nFirst time that this award was given to students\nMany lecturers at our faculty never get any teaching-award until retirement\n\n\nDevelopment of Pandoc-Filters for effective generation of lecture-slides for Mario Botsch (Leader “Workgroup Computer Graphics”) using Pandoc & reveal.js\n\nFramework: https://github.com/mbotsch/revealSlides\nExample: https://github.com/mbotsch/eLearning\nPandoc-Filters: https://github.com/mbotsch/pandoc-slide-filter",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Work-Experience"
+ ]
+ },
+ {
+ "objectID": "About/Experience.html",
+ "href": "About/Experience.html",
+ "title": "Highlights of my experiences in the programming world",
+ "section": "",
+ "text": "(as far as NDA and other things allow it)\n\n\n\nLearning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell\n\n\n\n\n\nI also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Highlights of my experiences in the programming world"
+ ]
+ },
+ {
+ "objectID": "About/Experience.html#haskell-enthusiast",
+ "href": "About/Experience.html#haskell-enthusiast",
+ "title": "Highlights of my experiences in the programming world",
+ "section": "",
+ "text": "Learning/Writing Haskell since ~2014\nCreated and held advanced Haskell-Lecture at my University\n\n\n\n\nMy Profile\nHaskell-Lecture\nCo-Founder of DataHaskell",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Highlights of my experiences in the programming world"
+ ]
+ },
+ {
+ "objectID": "About/Experience.html#gitea",
+ "href": "About/Experience.html#gitea",
+ "title": "Highlights of my experiences in the programming world",
+ "section": "",
+ "text": "I also have a gitea-instance where one can finde more current things and backups of old.\n\n\n\nAuthor of Eve-Online-Interface in yesod-auth-oauth2\nAuthor of “New Eden Accounting Tool” (neat), which is basically a ledger for Trading in the game Eve-Online\nDriver behind getting https://github.com/jgm/pandoc/issues/168 implemented and merged, because we needed it for our slide-filters (see [[Work]]# -> Development of Filters)\nAuthor of img2ascii - Small cli-tool for converting images into terminal-codes & ascii using JuicyPixels, because i always forget what is on the images over an ssh-connection -.-\nImplemented Array-Fusion and Recycling for subhask as layed out in Recycle your Arrays by Roman Leshchinskiy\nRaytracer in Haskell for my Computergraphics-Course\nimplementation of Densely Connected Bi-Clusters-Algorithm in Haskell (Paper)\nChemodiversity-Project at University during my masters. Complete with slideshow explaining everything.\nseveral other dead projects :D",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Highlights of my experiences in the programming world"
+ ]
+ },
+ {
+ "objectID": "About/Extracurricular.html",
+ "href": "About/Extracurricular.html",
+ "title": "Studium generale / University-Life",
+ "section": "",
+ "text": "(What I did at university besides studying :sunglasses: )\n\n\n\nStudent Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs\n\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Studium generale / University-Life"
+ ]
+ },
+ {
+ "objectID": "About/Extracurricular.html#committees-student-body",
+ "href": "About/Extracurricular.html#committees-student-body",
+ "title": "Studium generale / University-Life",
+ "section": "",
+ "text": "Student Member of Studienbeirat Informatik (Study-Profile Commission)\nStudent Member of Tutorenauswahlkommission (Tutor-Selection Committee)\n\nLeader Tutorenevaluation (Evaluation of Tutors)\n\nStudent Member of NWI-Master-Auswahlausschuss (Master-Application Committee for my course of study)\nStudent Member of NWI-Master-Prüfungsausschuss (Committee for Exam-disputes of my Master course)\nMember of the Admin-Team for the student-body pcs",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Studium generale / University-Life"
+ ]
+ },
+ {
+ "objectID": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
+ "href": "About/Extracurricular.html#ekvv-links-entries-in-the-electronic-course-catalog",
+ "title": "Studium generale / University-Life",
+ "section": "",
+ "text": "Fortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nLecture on YouTube (differs from link above)\nThis was the “silver chalk”-lecture\n[[FFPiH|more details on the lecture]]#\n\n\n\n\n\n\nRichtig Starten (Start Right!)\nTutor Introduction to Machine Learning (Tutor in this Lecture)\n\nWas awarded Tutoring-Award of the faculty\n\nRemade and updated slides for Computergraphics-Lecture\n\nLecture was awarded “silver chalk” among others things because of the updated slides.\n\n\n\n\n\n\nFortgeschrittene funktionale Programmierung in Haskell (Haskell-Lecture)\n\nSame as Summer 16\nTotally reworked Exercises accompanying the lecture\n[[FFPiH|more details on the lecture]]#",
+ "crumbs": [
+ "Home",
+ "Info",
+ "About me",
+ "Studium generale / University-Life"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Code Snippets/Monoid.html",
+ "href": "Coding/Haskell/Code Snippets/Monoid.html",
+ "title": "Monoid? Da war doch was…",
+ "section": "",
+ "text": "Stellen wir uns vor, dass wir eine Funktion schreiben, die einen String bekommt (mehrere Lines mit ACSII-Text) und dieses Wort-für-Wort rückwärts ausgeben soll. Das ist ein einfacher Einzeiler:\nWas passiert hier an Vodoo? Und was machen die ganzen wilden Zeichen da?\nGehen wir die Main zeilenweise durch: Wir lesen die Datei, die im ersten Kommandozeilen-Argument gegeben wird. getArgs hat folgende Signatur:\nWir bekommen als eine Liste der Argumente. Wir wollen nur das erste. Also machen wir head getArgs. Allerdings fliegt uns dann ein Fehler. head sieht nämlich so aus:\nIrgendwie müssen wird as in das IO bekommen. Hierzu gibt es fmap. Somit ist\nEin inline-Alias (um die Funktion links und das Argument rechts zu schreiben und sich ne Menge Klammern zu sparen) ist <$>. Somit ist schlussendlich der Inhalt der Datei aus dem ersten Argument (lazy) in ls.\nEine andere Möglichkeit sich das (in diesem Fall) zu merken, bzw. drauf zu kommen ist, dass [] AUCH ein Funktor (sogar eine Monade) ist. Man könnte das also auch so schreiben:\nfmap “packt” die Funktion quasi 1 Umgebung (Funktor, Monade, ..) weiter rein - Sei es nun in Maybe, Either oder irgendwas anderes.\nAlternatives (ausführliches) Beispiel am Ende.\nWenn wir uns die Signatur ansehen, dann haben wir nun\nreadFile will aber nun ein String haben. Man kann nun\nkann man auch “inline” mit =<< die Sachen “auspacken”.\nDie 2. Zeile lesen wir nun einfach “von hinten”, wie man das meistens tun sollte. Hier ist ein\nwas uns den Inhalt der Datei zeilenweise gibt. Mit jeder Zeile möchten wir nun folgendes machen:\nWenn wir uns die Signatur ansehen:\nDas mag im ersten Moment verwirren, daher noch die Signaturen der Einzelfunktionen:\nDa wir am Ende in der IO-Monade landen müssen wir das auf unsere Zeilen mit mapM statt map anwenden. Dies sorgt auch dafür, dass die Liste der reihe nach durchgegangen wird. mapM mit unserer Funktion schaut dann so aus:\neek! Das [IO ()] sieht ekelig aus. Wir haben eine Liste von IO-gar nichts. Das können wir eigentlich entsorgen. Da wir innerhalb der main-Funktion in einer IO-Monade sind, wollen wir IO () anstatt [IO ()] zurück haben.\nWenn wir uns jetzt erinnern, dass [] auch nur eine Monade ist und dass jede Monade ein Monoid ist, dann ist die Lösung einfach. Monoide haben eine “append”-funktion (mappend oder (<>) genannt). Wenn wir “nichts” an “nichts” anhängen, dann erhalten wir …. Trommelwirbel “nichts”! Wir müssen die [IO ()]-Liste also “nur noch” mit mappend falten. Hierzu gibt es schon eine vorgefertigte Funktion:\nWas genau die gewünschte Faltung macht. Wir müssen nun wieder fmap nehmen, da wir die Liste selbst falten wollen - und nicht map, welches auf den IO () innerhalb der Liste arbeiten würde. Durch die Faltung fällt die Liste nun auf IO () zusammen.\nViel Voodoo in wenig Code, aber wenn man sich dran gewöhnt hat, sind Monaden in Monaden auch nicht schlimm. Man muss sich immer nur richtig “rein” fmap’en.\nKleinen Tipp gab es noch: mapM_ macht genau das, was oben mit mconcat erreicht werden sollte. Somit kann man auch\nschreiben. Ich hab es aber mal wegen der klarheit oben so gelassen.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Code Snippets",
+ "Monoid? Da war doch was..."
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
+ "href": "Coding/Haskell/Code Snippets/Monoid.html#alternatives-fmap-beispiel",
+ "title": "Monoid? Da war doch was…",
+ "section": "Alternatives fmap-Beispiel",
+ "text": "Alternatives fmap-Beispiel\nNehmen wir als alternatives Beispiel mal an:\na :: IO Maybe State t\nUm Funktionen vom Typ\nf :: IO a -> IO a\nf a -- valide\nzu nehmen, brauchen wir nichts machen. Bei\nf' :: Maybe a -> Maybe a\nbrauchen wir 1 fmap, also ein\nf' a -- error\nf' <$> a\num eine Funktion\nf'' :: State t -> State t\nzu benutzen folglich:\nf'' a -- error\nf'' <$> a -- error\nfmap f'' <$> a",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Code Snippets",
+ "Monoid? Da war doch was..."
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Webapp-Example/Main.hs.html",
+ "href": "Coding/Haskell/Webapp-Example/Main.hs.html",
+ "title": "Webapp-Example: Main.hs",
+ "section": "",
+ "text": "Wie man das verwendet, siehe Webapp-Example.\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Webapp-Development in Haskell",
+ "Webapp-Example: Main.hs"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Webapp-Example/index.html",
+ "href": "Coding/Haskell/Webapp-Example/index.html",
+ "title": "Webapp-Development in Haskell",
+ "section": "",
+ "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Webapp-Development in Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
+ "href": "Coding/Haskell/Webapp-Example/index.html#definition-der-api",
+ "title": "Webapp-Development in Haskell",
+ "section": "",
+ "text": "Erster Schritt ist immer ein wünsch-dir-was bei der Api-Defenition.\nDie meisten Services haben offensichtliche Anforderungen (Schnittstellen nach draußen, Schnittstellen intern, …). Diese kann man immer sehr gut in einem Request -> Response-Model erfassen.\nUm die Anforderungen und Möglichkeiten des jeweiligen Services sauber zu erfassen und automatisiert zu prüfen, dummy-implementationen zu bekommen und vieles andere mehr, empfiehlt es sich den Openapi-generator zu nutzen.\nDiese Definition läuft über openapi-v3 und kann z.b. mit Echtzeit-Vorschau im http://editor.swagger.io/ erspielen. Per Default ist der noch auf openapi-v2 (aka swagger), kann aber auch v3.\nNach der Definition, was man am Ende haben möchte, muss man sich entscheiden, in welcher Sprache man weiter entwickelt. Ich empfehle aus verschiedenen Gründen primär 2 Sprachen: Python-Microservices (weil die ML-Libraries sehr gut sind, allerdings Änderungen meist schwer sind und der Code wenig robust - meist nur 1 API-Endpunkt pro service) und Haskell (Stabilität, Performace, leicht zu ändern, gut anzupassen).\nIm folgenden wird (aus offensichtlichen Gründen) nur auf das Haskell-Projekt eingegangen.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Webapp-Development in Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
+ "href": "Coding/Haskell/Webapp-Example/index.html#startprojekt-in-haskell",
+ "title": "Webapp-Development in Haskell",
+ "section": "Startprojekt in Haskell",
+ "text": "Startprojekt in Haskell\n\nErstellen eines neuen Projektes\nZunächst erstellen wir in normales Haskell-Projekt ohne Funktionalität & Firlefanz:\nstack new myservice\nDies erstellt ein neues Verzeichnis und das generelle scaffolding. Nach einer kurzen Anpassung der stack.yaml (resolver auf unserer setzen; aktuell: lts-17.4) fügen wir am Ende der Datei\nallow-newer: true\nghc-options:\n \"$locals\": -fwrite-ide-info\nein. Anschließend organisieren™ wir uns noch eine gute .gitignore und initialisieren das git mittels git init; git add .; git commit -m \"initial scaffold\"\n\n\nGenerierung der API\nDa die API immer wieder neu generiert werden kann (und sollte!) liegt sich in einem unterverzeichnis des Hauptprojektes.\nInitial ist es das einfachste ein leeres temporäres Verzeichnis woanders zu erstellen, die api-doc.yml hinein kopieren und folgendes ausführen:\nopenapi-generator generate -g haskell -o . -i api-doc.yml\nDieses erstellt einem dann eine komplette library inkl. Datentypen. Wichtig: Der Name in der api-doc sollte vom Namen des Services (oben myservice) abweichen - entweder in Casing oder im Namen direkt. Suffixe wie API schneidet der Generator hier leider ab. (Wieso das ganze? Es entstehen nachher 2 libraries, foo & fooAPI. Da der generator das API abschneidet endet man mit foo & foo und der compiler meckert, dass er nicht weiß, welche lib gemeint ist).\ndanach: wie gewohnt git init; git add .; git commit -m \"initial\". Auf dem Server der Wahl (github, gitea, gitlab, …) nun ein Repository erstellen (am Besten: myserviceAPI - nach Konvention ist alles auf API endend autogeneriert!) und den Anweisungen nach ein remote hinzufügen & pushen.\n\nWieder zurück im Haskell-Service\nIn unserem eigentlichen Service müssen wir nun die API einbinden. Dazu erstellen wir ein Verzeichnis libs (Konvention) und machen ein git submodule add <repository-url> libs/myserviceAPI\nGit hat nun die API in das submodul gepackt und wir können das oben erstellte temporäre Verzeichnis wieder löschen.\nAnschließend müssen wir stack noch erklären, dass wir die API da nun liegen haben und passen wieder die stack.yaml an, indem wir das Verzeichnis unter packages hinzufügen.\npackages:\n - .\n - libs/myserviceAPI # <<\nNun können wir in der package.yaml (oder myservice.cabal, falls kein hpack verwendet wird) unter den dependencies unsere API hinzufügen (name wie die cabal-Datei in libs/myserviceAPI).\n\n\n\nEinbinden anderer Microservices\nFunktioniert komplett analog zu dem vorgehen oben (ohne das generieren natürlich :grin:). stack.yaml editieren und zu den packages hinzufügen:\npackages:\n - .\n - libs/myserviceAPI\n - libs/myCoolMLServiceAPI\nin der package.yaml (oder der cabal) die dependencies hinzufügen und schon haben wir die Features zur Verfügung und können gegen diese Services reden.\n\n\nEntfernen von anderen Technologien/Microservices\nIn git ist das entfernen von Submodules etwas frickelig, daher hier ein copy&paste der GitHub-Antwort:\n## Remove the submodule entry from .git/config\ngit submodule deinit -f path/to/submodule\n\n## Remove the submodule directory from the superproject's .git/modules directory\nrm-rf .git/modules/path/to/submodule\n\n## Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule\ngit rm-f path/to/submodule\nFalls das nicht klappt, gibt es alternative Vorschläge unter dem Link oben.\n\n\nWoher weiss ich, was wo liegt? Dokumentation? Halloo??\nKeine Panik. Ein stack haddock --open hilft da. Das generiert die Dokumentation für alle in der package.yaml (oder cabal-file) eingetragenen dependencies inkl. aller upstream-dependencies. Man bekommt also eine komplette lokale Dokumentation von allem. Geöffnet wird dann die Paket-Startseite inkl. der direkten dependencies:\nEs gibt 2 wichtige Pfade im Browser:\n\n...../all/index.html - hier sind alle Pakete aufgeführt\n...../index.html - hier sind nur die direkten dependencies aufgeführt.\n\nWenn man einen lokalen Webserver startet kann man mittels “s” auch die interaktive Suche öffnen (Suche nach Typen, Funktionen, Signaturen, etc.). In Bash mit python3 geht das z.b. einfach über:\ncd $(stack path --local-doc-root)\npython3 -m SimpleHTTPServer 8000\nfirefox \"http://localhost:8000\"\n\n\nImplementation des Services und Start\n\nLoader/Bootstrapper\nGenerelles Vorgehen:\n\nin app/Main.hs: Hier ist quasi immer nur eine Zeile drin: main = myServiceMain\nGrund: Applications tauchen nicht im Haddock auf. Also haben wir ein “src”-Modul, welches hier nur geladen & ausgeführt wird.\nin src/MyService.hs: myServiceMain :: IO () definieren\n\nFür die Main kann man prinzipiell eine Main andere Services copy/pasten. Im folgenden eine Annotierte main-Funktion - zu den einzelnen Voraussetzungen kommen wir im Anschluss.\nMain.hs anzeigen\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE ScopedTypeVariables #-}\nmodule MyService where\n\n-- generische imports aus den dependencies/base, nicht in der prelude\nimport Codec.MIME.Type\nimport Configuration.Dotenv as Dotenv\nimport Control.Concurrent (forkIO, threadDelay)\nimport Control.Concurrent.Async\nimport Control.Concurrent.STM\nimport Control.Monad\nimport Control.Monad.Catch\nimport Control.Monad.Except\nimport Conversion\nimport Conversion.Text ()\nimport Data.Binary.Builder\nimport Data.String (IsString (..))\nimport Data.Time\nimport Data.Time.Clock\nimport Data.Time.Format\nimport Data.Default\nimport Network.HostName\nimport Network.HTTP.Client as HTTP hiding\n (withConnection)\nimport Network.HTTP.Types (Status, statusCode)\nimport Network.Mom.Stompl.Client.Queue\nimport Network.Wai (Middleware)\nimport Network.Wai.Logger\nimport Network.Wai.Middleware.Cors\nimport Network.Wai.Middleware.RequestLogger (OutputFormat (..),\n logStdout,\n mkRequestLogger,\n outputFormat)\nimport Servant.Client (mkClientEnv,\n parseBaseUrl)\nimport System.Directory\nimport System.Envy\nimport System.IO\nimport System.Log.FastLogger\nimport Text.PrettyPrint.GenericPretty\n\n-- generische imports, aber qualified, weil es sonst zu name-clashes kommt\n\nimport qualified Data.ByteString as BS\n-- import qualified Data.ByteString.Char8 as BS8\nimport qualified Data.ByteString.Lazy as LBS\nimport qualified Network.HTTP.Client.TLS as UseDefaultHTTPSSettings (tlsManagerSettings)\nimport qualified Network.Mom.Stompl.Client.Queue as AMQ\nimport qualified Network.Wai as WAI\n\n-- Handler für den MyServiceBackend-Typen und Imports aus den Libraries\nimport MyService.Handler as H -- handler der H.myApiEndpointV1Post implementiert\nimport MyService.Types -- weitere Type (s. nächste box)\nimport MyServiceGen.API as MS -- aus der generierten library\n\n\nmyServicemain :: IO ()\nmyServicemain = do\n -- .env-Datei ins Prozess-Environment laden, falls noch nicht von außen gesetzt\n void $ loadFile $ Dotenv.Config [\".env\"] [] False\n -- Config holen (defaults + overrides aus dem Environment)\n sc@ServerConfig{..} <- decodeWithDefaults defConfig\n -- Backend-Setup\n -- legt sowas wie Proxy-Server fest und wo man wie dran kommt. Benötigt für das Sprechen mit anderen Microservices\n let defaultHTTPSSettings = UseDefaultHTTPSSettings.tlsManagerSettings { managerResponseTimeout = responseTimeoutMicro $ 1000 * 1000 * myserviceMaxTimeout }\n createBackend url proxy = do\n manager <- newManager . managerSetProxy proxy\n $ defaultHTTPSSettings\n url' <- parseBaseUrl url\n return (mkClientEnv manager url')\n internalProxy = case myserviceInternalProxyUrl of\n \"\" -> noProxy\n url -> useProxy $ HTTP.Proxy (fromString url) myserviceInternalProxyPort\n -- externalProxy = case myserviceExternalProxyUrl of\n -- \"\" -> noProxy\n -- url -> useProxy $ HTTP.Proxy (fromString url) myserviceExternalProxyPort\n\n -- Definieren & Erzeugen der Funktionen um die anderen Services anzusprechen.\n calls <- (,)\n <$> createBackend myserviceAUri internalProxy\n <*> createBackend myserviceBUri internalProxy\n\n -- Logging-Setup\n hSetBuffering stdout LineBuffering\n hSetBuffering stderr LineBuffering\n\n\n -- Infos holen, brauchen wir später\n myName <- getHostName\n today <- formatTime defaultTimeLocale \"%F\" . utctDay <$> getCurrentTime\n\n\n -- activeMQ-Transaktional-Queue zum schreiben nachher vorbereiten\n amqPost <- newTQueueIO\n\n\n -- bracket a b c == erst a machen, ergebnis an c als variablen übergeben. Schmeisst c ne exception/wird gekillt/..., werden die variablen an b übergeben.\n bracket\n -- logfiles öffnen\n (LogFiles <$> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".info\") AppendMode\n <*> openFile (if myserviceDebug then \"/logs/myservice-\"<>myName<>\"-\"<>today<>\".debug\" else \"/dev/null\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".error\") AppendMode\n <*> openFile (\"/logs/myservice-\"<>myName<>\"-\"<>today<>\".timings\") AppendMode\n )\n -- und bei exception/beendigung schlißen.h\n (\\(LogFiles a b c d) -> mapM_ hClose [a,b,c,d])\n $ \\logfiles -> do\n\n\n -- logschreibe-funktionen aliasen; log ist hier abstrakt, iolog spezialisiert auf io.\n let log = printLogFiles logfiles :: MonadIO m => [LogItem] -> m ()\n iolog = printLogFilesIO logfiles :: [LogItem] -> IO ()\n\n\n -- H.myApiEndpointV1Post ist ein Handler (alle Handler werden mit alias H importiert) und in einer eigenen Datei\n -- Per Default bekommen Handler sowas wie die server-config, die Funktionen um mit anderen Services zu reden, die AMQ-Queue um ins Kibana zu loggen und eine Datei-Logging-Funktion\n -- Man kann aber noch viel mehr machen - z.b. gecachte Daten übergeben, eine Talk-Instanz, etc. pp.\n server = MyServiceBackend{ myApiEndpointV1Post = H.myApiEndpointV1Post sc calls amqPost log\n }\n config = MS.Config $ \"http://\" ++ myserviceHost ++ \":\" ++ show myservicePort ++ \"/\"\n iolog . pure . Info $ \"Using Server configuration:\"\n iolog . pure . Info $ pretty sc { myserviceActivemqPassword = \"******\" -- Do NOT log the password ;)\n , myserviceMongoPassword = \"******\"\n }\n -- alle Services starten (Hintergrund-Aktionen wie z.b. einen MongoDB-Dumper, einen Talk-Server oder wie hier die ActiveMQ\n void $ forkIO $ keepActiveMQConnected sc iolog amqPost\n -- logging-Framework erzeugen\n loggingMW <- loggingMiddleware\n -- server starten\n if myserviceDebug\n then runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]})) . loggingMW . logStdout) server\n else runMyServiceMiddlewareServer config (cors (\\_ -> Just (simpleCorsResourcePolicy {corsRequestHeaders = [\"Content-Type\"]}))) server\n\n\n-- Sollte bald in die Library hs-stomp ausgelagert werden\n-- ist ein Beispiel für einen ActiveMQ-Dumper\nkeepActiveMQConnected :: ServerConfig -> ([LogItem] -> IO ()) -> TQueue BS.ByteString -> IO ()\nkeepActiveMQConnected sc@ServerConfig{..} printLog var = do\n res <- handle (\\(e :: SomeException) -> do\n printLog . pure . Error $ \"Exception in AMQ-Thread: \"<>show e\n return $ Right ()\n ) $ AMQ.try $ do -- catches all AMQ-Exception that we can handle. All others bubble up.\n printLog . pure . Info $ \"AMQ: connecting...\"\n withConnection myserviceActivemqHost myserviceActivemqPort [ OAuth myserviceActivemqUsername myserviceActivemqPassword\n , OTmo (30*1000) {- 30 sec timeout -}\n ]\n [] $ \\c -> do\n let oconv = return\n printLog . pure . Info $ \"AMQ: connected\"\n withWriter c \"Chaos-Logger for Kibana\" \"chaos.logs\" [] [] oconv $ \\writer -> do\n printLog . pure . Info $ \"AMQ: queue created\"\n let postfun = writeQ writer (Type (Application \"json\") []) []\n void $ race\n (forever $ atomically (readTQueue var) >>= postfun)\n (threadDelay (600*1000*1000)) -- wait 10 Minutes\n -- close writer\n -- close connection\n -- get outside of all try/handle/...-constructions befor recursing.\n case res of\n Left ex -> do\n printLog . pure . Error $ \"AMQ: \"<>show ex\n keepActiveMQConnected sc printLog var\n Right _ -> keepActiveMQConnected sc printLog var\n\n\n-- Beispiel für eine Custom-Logging-Middleware.\n-- Hier werden z.B. alle 4xx-Status-Codes inkl. Payload ins stdout-Log geschrieben.\n-- Nützlich, wenn die Kollegen ihre Requests nicht ordentlich schreiben können und der Server das Format zurecht mit einem BadRequest ablehnt ;)\nloggingMiddleware :: IO Middleware\nloggingMiddleware = liftIO $ mkRequestLogger $ def { outputFormat = CustomOutputFormatWithDetails out }\n where\n out :: ZonedDate -> WAI.Request -> Status -> Maybe Integer -> NominalDiffTime -> [BS.ByteString] -> Builder -> LogStr\n out _ r status _ _ payload _\n | statusCode status < 300 = \"\"\n | statusCode status > 399 && statusCode status < 500 = \"Error code \"<>toLogStr (statusCode status) <>\" sent. Request-Payload was: \"<> mconcat (toLogStr <$> payload) <> \"\\n\"\n | otherwise = toLogStr (show r) <> \"\\n\"\n\n\nWeitere Instanzen und Definitionen, die der Generator (noch) nicht macht\nIn der Myservice.Types werden ein paar hilfreiche Typen und Typ-Instanzen definiert. Im Folgenden geht es dabei um Dinge für:\n\nEnvy\n\nLaden von $ENV_VAR in Datentypen\nDefinitionen für Default-Settings\n\nServerConfig\n\nDefinition der Server-Konfiguration & Benennung der Environment-Variablen\n\nExtraTypes\n\nggf. Paketweite extra-Typen, die der Generator nicht macht, weil sie nicht aus der API kommen (z.B. cache)\n\nOut/BSON-Instanzen\n\nDer API-Generator generiert nur wenige Instanzen automatisch (z.B. aeson), daher werden hier die fehlenden definiert.\nBSON: Kommunikation mit MongoDB\nOut: pretty-printing im Log\n\nNur nötig, wenn man pretty-printing via Out statt über Generics wie z.b. pretty-generic oder die automatische Show-Instanz via prerryShow macht.\n\n\n\nTypes.hs anzeigen\n{-# OPTIONS_GHC -Wno-orphans #-}\n{-# OPTIONS_GHC -Wno-name-shadowing #-}\n{-# LANGUAGE DeriveAnyClass #-}\n{-# LANGUAGE DeriveFunctor #-}\n{-# LANGUAGE DeriveGeneric #-}\n{-# LANGUAGE DerivingVia #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE MultiParamTypeClasses #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE RankNTypes #-}\n{-# LANGUAGE RecordWildCards #-}\nmodule MyService.Types where\n \nimport Data.Aeson (FromJSON, ToJSON)\nimport Data.Text\nimport Data.Time.Clock\nimport GHC.Generics\nimport System.Envy\nimport Text.PrettyPrint (text)\nimport Text.PrettyPrint.GenericPretty\n \n-- Out hat hierfür keine Instanzen, daher kurz eine einfach Definition.\ninstance Out Text where\n doc = text . unpack\n docPrec i a = text $ showsPrec i a \"\"\n \ninstance Out UTCTime where\n doc = text . show\n docPrec i a = text $ showsPrec i a \"\"\n \n-- Der ServerConfig-Typ. Wird mit den defaults unten initialisiert, dann mit den Variablen aus der .env-Datei überschrieben und zum Schluss können Serveradmins diese via $MYSERVICE_FOO nochmal überschreiben.\ndata ServerConfig = ServerConfig\n { myserviceHost :: String -- ^ Environment: $MYSERVICE_HOST\n , myservicePort :: Int -- ^ Environment: $MYSERVICE_PORT\n , myserviceMaxTimeout :: Int -- ^ Environment: $MYSERVICE_MAX_TIMEOUT\n , myserviceInternalProxyUrl :: String -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_URL\n , myserviceInternalProxyPort :: Int -- ^ Environment: $MYSERVICE_INTERNAL_PROXY_PORT\n , myserviceExternalProxyUrl :: String -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_URL\n , myserviceExternalProxyPort :: Int -- ^ Environment: $MYSERVICE_EXTERNAL_PROXY_PORT\n , myserviceActivemqHost :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_HOST\n , myserviceActivemqPort :: Int -- ^ Environment: $MYSERVICE_ACTIVEMQ_PORT\n , myserviceActivemqUsername :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_USERNAME\n , myserviceActivemqPassword :: String -- ^ Environment: $MYSERVICE_ACTIVEMQ_PASSWORD\n , myserviceMongoUsername :: String -- ^ Environment: $MYSERVICE_MONGO_USERNAME\n , myserviceMongoPassword :: String -- ^ Environment: $MYSERVICE_MONGO_PASSWORD\n , myserviceDebug :: Bool -- ^ Environment: $MYSERVICE_DEBUG\n } deriving (Show, Eq, Generic)\n \n-- Default-Konfigurations-Instanz für diesen Service.\ninstance DefConfig ServerConfig where\n defConfig = ServerConfig \"0.0.0.0\" 8080 20\n \"\"\n \"\"\n \"\"\n 0\n \"\"\n 0\n \"\"\n 0\n \"\"\n \"\"\n \"\"\n \"\"\n False\n \n-- Kann auch aus dem ENV gefüllt werden\ninstance FromEnv ServerConfig\n-- Und hübsch ausgegeben werden.\ninstance Out ServerConfig\n \n \ninstance Out Response\ninstance FromBSON Repsonse -- FromBSON-Instanz geht immer davon aus, dass alle keys da sind (ggf. mit null bei Nothing).\n\n\nWas noch zu tun ist\nDen Service implementieren. Einfach ein neues Modul aufmachen (z.B. MyService.Handler oder MyService.DieserEndpunktbereich/MyService.JenerEndpunktbereich) und dort die Funktion implementieren, die man in der Main.hs benutzt hat. In dem Handler habt ihr dann keinen Stress mehr mit Validierung, networking, logging, etc. pp. weil alles in der Main abgehandelt wurde und ihr nur noch den “Happy-Case” implementieren müsst. Beispiel für unseren Handler oben:\nmyApiEndpointV1Post :: MonadIO m => ServerConfig -> (ClientEnv,ClientEnv) -> TQueue BS.ByteString -> ([LogItem] -> IO ()) -> Request -> m Response\nmyApiEndpointV1Post sc calls amqPost log req = do\n liftIO . log $ [Info $ \"recieved \"<>pretty req] -- input-logging\n liftIO . atomically . writeTQueue . LBS.toStrict $ \"{\\\"hey Kibana, i recieved:\\\"\" <> A.encode (pretty req) <> \"}\" -- log in activeMQ/Kibana\n\n\n --- .... gaaaanz viel komplizierter code um die Response zu erhalten ;)\n let ret = Response 1337 Nothing -- dummy-response ;)\n -- gegeben wir haben eine gültige mongodb-pipe;\n -- mehr logik will ich in die Beispiele nicht packen.\n -- Man kann die z.b. als weiteren Wert in einer TMVar (damit man sie ändern & updaten kann) an die Funktion übergeben.\n liftIO . access pipe master \"DatabaseName\" $ do\n ifM (auth (myServiceMongoUsername sc) (myServiceMongoPassword sc)) (return ()) (liftIO . printLog . pure . Error $ \"MongoDB: Login failed.\")\n save \"DatabaseCollection\" [\"_id\" =: 1337, \"entry\" =: ret] -- selbe id wie oben ;)\n return ret\nDiese dummy-Antwort führt auf, wie gut man die ganzen Sachen mischen kann.\n\nLogging in die Dateien/stdout - je nach Konfiguration\nLogging von Statistiken in Kibana\nSpeichern der Antwort in der MongoDB\nGenerieren einer Serverantwort und ausliefern dieser über die Schnittstelle\n\n\n\nTipps & Tricks\n\nDateien, die statisch ausgeliefert werden sollen\nHierzu erstellt man ein Verzeichnis static/ (Konvention; ist im generator so generiert, dass das ausgeliefert wird). Packt man hier z.b. eine index.html rein, erscheint die, wenn man den Service ansurft.\n\n\nWie bekomme ich diese fancy Preview hin?\nDer Editor, der ganz am Anfang zum Einsatz gekommen ist, braucht nur die api-doc.yml um diese Ansicht zu erzeugen. Daher empfiehlt sich hier ein angepasster Fork davon indem die Pfade in der index.html korrigiert sind. Am einfachsten (und von den meisten services so benutzt): In meiner Implementation liegt dann nach dem starten auf http://localhost:PORT/ui/ und kann direkt dort getestet werden.\n\n\nWie sorge ich für bessere Warnungen, damit der Compiler meine Bugs fängt?\nstack build --file-watch --ghc-options '-freverse-errors -W -Wall -Wcompat' --interleaved-output\nWas tut das?\n\n--file-watch: automatisches (minimales) kompilieren bei dateiänderungen\n--ghc-options\n\n-freverse-errors: Fehlermeldungen in umgekehrter Reihenfolge (Erster Fehler ganz unten; wenig scrollen )\n-W: Warnungen an\n-Wall: Alle sinnvollen Warnungen an (im gegensatz zu -Weverything, was WIRKLICH alles ist )\n-Wcompat: Warnungen für Sachen, die in der nächsten Compilerversion kaputt brechen werden & vermieden werden sollten\n\n--interleaved-output: stack-log direkt ausgeben & nicht in Dateien schreiben und die dann am ende zusammen cat'en.\n\nUm pro Datei Warnungen auszuschalten (z.B. weil man ganz sicher weiss, was man tut -.-): {-# OPTIONS_GHC -Wno-whatsoever #-} als pragma in die Datei.\nIdealerweise sollte das Projekt keine Warnungen erzeugen.\n\n\n\n\nDeployment\nAls Beispiel sei hier ein einfaches Docker-Build mit Jenkins-CI gezeigt, weil ich das aus Gründen rumliegen hatte. Kann man analog in fast alle anderen CI übersetzen.\n\nDocker\nDie angehängten Scripte gehen von einer Standard-Einrichtung aus (statische Sachen in static, 2-3 händische Anpassungen auf das eigene Projekt nach auspacken). Nachher liegt dann auch unter static/version die gebaute Versionsnummer & kann abgerufen werden. In der Dockerfile.release und der Jenkinsfile müssen noch Anpassungen gemacht werden. Konkret:\n\nin der Dockerfile.release: alle <<<HIER>>>-Stellen sinnvoll befüllen\nin der Jenkinsfile die defs für “servicename” und “servicebinary” ausfüllen. Binary ist das, was bei stack exec aufgerufen wird; name ist der Image-Name für das docker-repository.\n\n\n\nJenkins\nÄnderungen die dann noch gemacht werden müssen:\n\ngit-repository URL anpassen\nEnvironment-Vars anpassen ($BRANCH = test & live haben keine zusatzdinger im docker-image-repository; ansonsten hat das image $BRANCH im Namen)\n\nWenn das fertig gebaut ist, liegt im test/live-repository ein docker-image namens servicename:version.\n\n\n\nOMG! Ich muss meine API ändern. Was mache ich nun?\n\napi-doc.yml bearbeiten, wie gewünscht\nmittels generator die Api & submodule neu generieren\nggf. custom Änderungen übernehmen (:Gitdiffsplit hilft)\nAlle Compilerfehler + Warnungen in der eigentlichen Applikation fixen\nIf it comipilez, ship it! (Besser nicht :grin:)",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Webapp-Development in Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html",
+ "href": "Coding/Haskell/Advantages.html",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "Gründe Haskell zu nutzen und wo Vorteile liegen.\n\n\n\nThe Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness\n\n\n\n\n\nPapers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers\n\n\n\n\n\nTom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)\n\n\n\n\n\nFunctional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett\n\n\n\n\n\nHaskell fast and hard\nCounterexamples for Typeclasses",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html#talks",
+ "href": "Coding/Haskell/Advantages.html#talks",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "The Future is parallel\nLenses (Registrierung nötig - kostenfrei), siehe auch: Lenses\nRunning a Startup on Haskell\nWe’re doing it all wrong - A Long-Term Scala-Compiler-Developer quits his job after years and tells why Scala is a mess.\nMonads explained in Javascript\nVinyl Records with Slides\nThinking with Laziness",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html#bücherpaper",
+ "href": "Coding/Haskell/Advantages.html#bücherpaper",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "Papers on STM\nTackling the awkward squad\nParallel and Concurrent Programming in Haskell\nSlides of a Quickcheck-Talk\nUnderstanding F-Algebras schöne Erklärung. Man könnte danach anfangen den Morphismen-zoo zu verstehen…\nMonad Transformers",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html#funny-talks",
+ "href": "Coding/Haskell/Advantages.html#funny-talks",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "Tom LaGatta on Category-Theory\nUnifying Structured Recursion Schemes aka. [[Morphisms|The Morphism-Zoo]]\nHole-Driven-Development Teaser (Enthusiasticon, raichoo)",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html#unsortedunseen",
+ "href": "Coding/Haskell/Advantages.html#unsortedunseen",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "Functional Reactive Programming\nDiagrams: Declarative Vector Graphics in Haskell\nLenses, Folds & Traversels by Edward Kmett\nDiscrimination is Wrong by Edward Kmett",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
+ {
+ "objectID": "Coding/Haskell/Advantages.html#tutorials",
+ "href": "Coding/Haskell/Advantages.html#tutorials",
+ "title": "Talks und Posts zu Haskell",
+ "section": "",
+ "text": "Haskell fast and hard\nCounterexamples for Typeclasses",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Coding",
+ "Haskell",
+ "Talks und Posts zu Haskell"
+ ]
+ },
{
"objectID": "Health/Issues.html",
"href": "Health/Issues.html",
@@ -1285,81 +1220,146 @@
]
},
{
- "objectID": "Writing/coding-age-ai.html",
- "href": "Writing/coding-age-ai.html",
- "title": "Field Report: Coding in the Age of AI with Cursor",
+ "objectID": "Writing/documentation.html",
+ "href": "Writing/documentation.html",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
"section": "",
- "text": "In the fast eveolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize nieche problems that can benchmarked[2]1.\n1 Like all decent humans i ain’t got time to read up on everything - so a big shoutout to [3] for doing thorough reviews on ML-topics and linking the respective papers!2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verifiedI personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.\nTherefore see this “Field Guide” for what it is: A current state of things that work for at least 1 individuum in exactly this ecosystem at this point in time.",
+ "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering [1, 2] und damit einhergehender Prinzipien wie die des Endings-Projekts für digitale Langlebigkeit [3] und FAIR4RS-Prinzipien[4].\nZiel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen[5].\nIm Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
- "objectID": "Writing/coding-age-ai.html#introduction",
- "href": "Writing/coding-age-ai.html#introduction",
- "title": "Field Report: Coding in the Age of AI with Cursor",
+ "objectID": "Writing/documentation.html#einleitung",
+ "href": "Writing/documentation.html#einleitung",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
"section": "",
- "text": "In the fast eveolving field of AI there is a clear lack of reports on “what really works”. Some techniques hailed as revolution (like the DeepSeek Aha-Moment[1]) for unlimited potential were soon realized to “just” optimize nieche problems that can benchmarked[2]1.\n1 Like all decent humans i ain’t got time to read up on everything - so a big shoutout to [3] for doing thorough reviews on ML-topics and linking the respective papers!2 i.e. the “base model” nearly all papers tested their finding on (qwen-series) also gets better with RLVR-optimization if rewards are random instead of verifiedI personally think it is an exercise in futility to get a current theoretical overview for forming a decent grounded opinion on the state of things. Even before one is done analyzing the literature, crossrefercencing and collecting evidence and then finally formulating methods and implementing them, the next revolution comes around that could put everything on its head again. In the afromentioned example the community went from “Reasoning is the solution” in January[1] over first critical views in March[4] to doubts on that claims validity of generating concepts previously not present in the base model in May[5] to complete ad-absurdum in June[2]2.\nTherefore see this “Field Guide” for what it is: A current state of things that work for at least 1 individuum in exactly this ecosystem at this point in time.",
+ "text": "Die Dokumentation von Forschungssoftware ist entscheidend, um wissenschaftliche Ergebnisse nachvollziehbar und Software für andere nutzbar zu machen. Insbesondere in den Digital Humanities (etwa in der Geschichtswissenschaft) entwickeln Forschende neben Forschung und Lehre oft eigene Software – meist unter hohem Zeitdruck und ohne formale Ausbildung in Softwareentwicklung. Häufig bleibt die Dokumentation deshalb minimal oder unvollständig, was dazu führt, dass andere (und sogar die Autor*innen selbst) viel Zeit aufwenden müssen, um den Code zu verstehen und anzuwenden. Dabei gilt gute Dokumentation als zentrale Voraussetzung, um Forschungssoftware auffindbar, nachvollziehbar und wiederverwendbar zu machen.\nDieser Anforderungskatalog richtet sich an Forschende, die keine Vollzeit-Programmierer sind, und soll wissenschaftlich fundierte Richtlinien für die Dokumentation von Forschungssoftware liefern. Die Empfehlungen berücksichtigen Best Practices des Research Software Engineering [1, 2] und damit einhergehender Prinzipien wie die des Endings-Projekts für digitale Langlebigkeit [3] und FAIR4RS-Prinzipien[4].\nZiel ist es, ein praxistaugliches Gerüst bereitzustellen, das – trotz Zeitknappheit – die wesentlichen Dokumentationsaspekte abdeckt, um sowohl die Nachvollziehbarkeit der Ergebnisse als auch eine Weiterverwendung der Software zu ermöglichen[5].\nIm Folgenden werden die Anforderungen an Inhalt, Format und Umfang der Dokumentation definiert, geeignete (teil-)automatisierte Dokumentationswerkzeuge diskutiert und Best Practices in Form von Vorlagen und Checklisten vorgestellt.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
- "objectID": "Writing/coding-age-ai.html#how-to-program-with-cursor",
- "href": "Writing/coding-age-ai.html#how-to-program-with-cursor",
- "title": "Field Report: Coding in the Age of AI with Cursor",
- "section": "How to program with Cursor",
- "text": "How to program with Cursor\nIn essence Cursor is “just” a fork of Microsofts VSCode with some added functionality: Automatically injecting files into LLM-Prompts, offering tool-aware LLMs to use MCPs, read the filesystem, execute arbitrary commands in the shell (either automomatically or after permission), getting feedback from the editor (i.e. installed linters, language-servers etc.) and thus have the same (or even better) information/tools available as the programmer in front of the screen.\n\nCapabilities / General procedure\nThe main issue is now: theoretically agentic IDEs can get all information - practically it is limited directly by token-window sizes, costs of these queries; and indirectly by outsourced costs like environmental impacts, data-security, etc. The suppliers of such services can claim privacy as much as they want - it can’t be proven and (especially under US-Law) is not even possible to resist lawful orders (including the gag-order to not talk about these).\nIn practise one feels the direct pain points more severly. Some regular examples include generating redundant code, because the current context was not aware of utility-modules and functions it could use - leading to huge technical debt in no time.\nTherefore my preferred workflow is to “think bigger”. Imagine being a product owner of a huge, sluggish company. The left hand never knows what the right hand does or has done (i.e. the LLM forgetting things already exist in the codebase), everything has to be rigorous defined, specified and enforced. Some people reported good results with Test-Driven-Development (TDD) - but in my experience these things only prevent regressions and not proactively enforce the desired agent behaviour.\n\n\nLessons from Project Management\nThis may be a duh!-Moment for people longer in Software-Engineering, but most of the time is getting the specifications of what you want to build right. Asking questions. Interviewing stakeholders. Gathering user experience reports. Most of it is not actually writing code - but deciding on what to write and how.\nFor this i created some rules in my workflow that interleave with their responsibilities and outcomes. Especially in the planning phase the LLM is encouraged to ask questions, find edge-cases or even look at other implementations. One could also think of letting the agent do a websearch, read reports and forums on how good which framework works and then arguments on why this framework is good - and also why it is not good. The last decision of all pro/contra is by the actual human.\nThe main theme always follows a similar pattern:\n\nA need is found.\nThis could be a bug, a feature, some changes to existing behaviour etc.\nAn investigation is launched, yielding a Product Requirement Document (PRD).\nThis dives into the codebase to asses the current state of things. Maybe some bugs are obvious and easily fixed.\nThis formalizes that the LLM understood what should be done and especially what is out of scope.\nPin the desired behaviour in a Specification.\nEither this means changing currently established specifications (i.e. bug/chang) or writing complete new ones (i.e. feature).\nInvestigate Spec-Compliance.\nAgain the agentlooks at the codebase to identify where things should change and how. Also recommendation are made on how it could achieve the goal.\nGenerate Tasks.\nFrom the compliance-report of spec-deviations (either from a bug or from a new/changed spec) finally a Plan to fix everything can be derived (think: Sprint-Planning).\nNOTE: Up to here the agent never touched the code.\nFinally Implement the change.\nThis is most often the most trivial step. Everything is known and formulated for “simple” Agents to just follow. It names files needed, specs to follow, guidelines on how to do things in the repository etc.",
+ "objectID": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
+ "href": "Writing/documentation.html#inhaltliche-anforderungen-an-die-dokumentation",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Inhaltliche Anforderungen an die Dokumentation",
+ "text": "Inhaltliche Anforderungen an die Dokumentation\nEin zentrales Problem in der Dokumentation wissenschaftlicher Software ist oft das fehlende Big Picture, also eine klare Darstellung des Was und Warum. Die Dokumentation sollte daher alle Informationen abdecken, die zum Verstehen, Nutzen und Weiterentwickeln der Software nötig sind[3, 6]. Insbesondere sind folgende Inhalte essenziell:\n\nZiel und Zweck der Software (Statement of Need)\nBeschreiben Sie was die Software tut und warum sie entwickelt wurde. Nennen Sie den wissenschaftlichen Zweck, das Forschungsproblem oder die Fragestellung, die mit der Software adressiert wird, sowie die Zielgruppe (wer soll sie nutzen?). Dieser Kontext hilft anderen, den Nutzen der Software einzuschätzen. Eine klare Problem- und Zielbeschreibung richtet sich auch nach dem Umfeld ähnlicher Lösungen – falls es bereits etablierte Tools gibt, sollte die Dokumentation die eigene Herangehensweise einordnen (z. B. was die Software anders oder besser macht).Beispiel: “Dieses Tool extrahiert Personen-Netzwerke aus historischen Briefkorpora, um sozialwissenschaftliche Analysen zu ermöglichen.”\n\n\nInput-/Output-Spezifikation und Datenbeschreibung\nDokumentieren Sie alle Eingabeformate, Ausgabedaten und verwendeten Datensätze. Nutzer*innen müssen wissen, welche Daten die Software erwartet (Dateiformate, Schnittstellen, Parameter) und welche Ergebnisse sie produziert. Idealerweise werden Beispiele angegeben: z. B. Beispiel-Dateien oder -Parameter und die korrespondierende Ausgabe.\nFalls die Software mit bestimmten Forschungsdaten arbeitet, beschreiben Sie diese Daten und ihre Struktur. Dies umfasst die Datenmodelle (etwa wichtige Felder, deren Bedeutung und kontrollierte Vokabulare) und Annahmen über die Daten.\nGemäß den ENDINGS-Prinzipien sollte die Datenstruktur auch in einem statischen Dokument festgehalten und der Software beigelegt sein – so bleibt nachvollziehbar, wie die Software die Daten interpretiert [3]. Eine Tabelle oder Auflistung der Eingabefelder und Ausgabegrößen mit kurzen Beschreibungen erhöht die Klarheit. Beispiel: “Eingabedatei: CSV mit Spalten Autor, Empfänger, …; Ausgabe: JSON-Datei mit Netzwerk-Metriken pro Briefwechsel.”\nGerade für JSON-Dateien bietet es sich an ggf. auch ein formelle Spezifikation via JSON-Schema an.\n\n\nCode-Abhängigkeiten und technische Voraussetzungen\nListen Sie alle Abhängigkeiten (Dependencies) der Software auf. Dazu gehören verwendete Programmiersprachen/Versionen, erforderliche Bibliotheken oder Frameworks, und sonstige Systemvoraussetzungen (z. B. Betriebssystem, Mindesthardware, Datenbank-Versionen). Wichtig ist, wie diese Abhängigkeiten installiert werden können. Optimal ist eine automatisierte Installationsroutine (z. B. ein requirements.txt für Python oder ein Paketmanager-Befehl). In jedem Fall sollte die Dokumentation mindestens Schritt-für-Schritt-Installationsanleitungen enthalten (inklusive evtl. benötigter Vorkenntnisse, z. B. “Python 3 erforderlich”). Beispiel: “Benötigt Python 3.9 und die Bibliotheken Pandas und NetworkX. Installation: pip install -r requirements.txt.” Falls spezielle technische Voraussetzungen bestehen – etwa Zugriff auf bestimmte Hardware, ein Hochleistungsrechner oder große Speicherkapazitäten – sind diese zu nennen.\n\n\nWissenschaftlicher Hintergrund und theoretischer Kontext\nDa es sich um Forschungssoftware handelt, sollten Sie den wissenschaftlichen Kontext 1 offenlegen. Das heißt, erklären Sie die grundlegenden Methoden, Algorithmen oder Modelle, die in der Software umgesetzt sind, zumindest in Überblicksform.\n1 Dieser Hintergrundteil unterscheidet Forschungssoftware-Dokumentation von rein kommerzieller Dokumentation: Es geht nicht nur um wie man das Tool benutzt, sondern auch warum es so funktioniert (Stichwort Nachvollziehbarkeit).Verweisen Sie auf relevante Publikationen oder Theorien, damit andere die wissenschaftliche Grundlage nachvollziehen können. Halten Sie diesen Abschnitt aber prägnant – Details gehören in die Forschungsarbeit selbst.Beispielsweise: “Die Implementierung folgt dem Algorithmus von Müller et al. (2019) zur Netzwerkanalyse historischer Korrespondenz.”\nWichtig ist, dass die Dokumentation den Brückenschlag zwischen Code und Forschung herstellt. Da viele Wissenschaftler*innen zentrale Aspekte lieber in ihren Artikeln dokumentieren, sollte in der Software-Dokumentation zumindest eine Zusammenfassung mit Querverweis erfolgen. So wissen Nutzer*innen, unter welchen Annahmen oder Theorien das Tool funktioniert.\n\n\nBekannte Limitationen, Annahmen und Fehlermeldungen\nGeben Sie ehrlich Auskunft über die Grenzen der Software:\n\nWelche Fälle werden nicht abgedeckt?\nWelche Annahmen über die Daten oder Anwendungsszenarien werden getroffen?\n\n Solche Hinweise verhindern Fehlanwendungen und sparen Nutzern Zeit.Beispielsweise: “funktioniert nur für Deutschsprachige Texte” oder “maximale Datenmenge 1 Mio. Datensätze, da Speicherbegrenzung”\nFalls es bekannte Bugs oder Workarounds gibt, sollten diese ebenfalls (etwa in einer FAQ oder einem Abschnitt “Bekannte Probleme”) erwähnt werden.\nAuch aussagekräftige Fehlermeldungen im Programm selbst sind eine Form von Dokumentation: Sie sollten nicht nur kryptisch abbrechen, sondern dem/der Anwender*in idealerweise mitteilen, was schiefging und bestenfalls direkt wie es behoben werden kann. Solche in den Code integrierten Hinweise ergänzen die schriftliche Dokumentation und tragen zur besseren Nutzbarkeit bei.Beispiel: “Fehler: Ungültiges Datum im Feld XY – bitte Format TT/MM/JJJJ verwenden.”\n\n\nWeiterentwicklung und Beitragsmöglichkeiten\nObwohl viele Digital-Humanities-Tools primär von Einzelpersonen genutzt werden, sollte dennoch angegeben werden, wie andere ggf. zur Software beitragen oder Support erhalten können. Ein kurzer Hinweis auf den Issue-Tracker (z. B. “Fehler bitte über GitHub-Issues melden”) oder auf die Kontaktmöglichkeit zum*zur Autor*in (E-Mail) gehört dazu.\nEbenso können Community Guidelines skizziert werden: etwa Code-Standards oder ein Verhaltenskodex, falls Beiträge erwartet werden. Für kleinere Projekte reicht oft ein Satz wie “Beiträge durch Pull Requests sind willkommen; bei Fragen wenden Sie sich an…”. 2\n2 Dieser Aspekt muss nicht umfangreich sein, zeigt aber Offenheit und sorgt dafür, dass im Falle von Rückfragen die Hürde für Kontaktaufnahme niedrig ist.\n\nProjekt-Metadaten (Lizenz, Zitation, Version)\nTeil der Dokumentation sind auch formale Informationen, die im Repository leicht zugänglich sein sollten[6]. Lizenzinformationen klären die rechtlichen Bedingungen der Nutzung und Weiterverbreitung. Es ist Best Practice, eine LICENSE-Datei beizulegen, aber auch in der README kurz zu erwähnen, unter welcher Lizenz die Software steht. Für Forschungssoftware empfiehlt sich eine offene Lizenz (z. B. MIT, BSD oder Apache 2.0 für Code, CC-BY für Daten), um Nachnutzung nicht zu behindern.\nZudem sollte angegeben werden, wie die Software zitiert werden kann (z. B. DOI, Paper-Referenz). Ein eigener Abschnitt “Zitation” oder eine CITATION-Datei beschreibt, welche Publikation oder welcher DOI bei Verwendung der Software in wissenschaftlichen Arbeiten anzugeben ist. Dies erhöht die akademische Sichtbarkeit und stellt sicher, dass Autor*innen Credits für ihre Software bekommen [7].\nSchließlich ist es sinnvoll, eine Versionsnummer der Software zu nennen (idealerweise in README und im Tool selbst), damit Nutzer wissen, auf welche Ausgabe sich die Dokumentation bezieht – insbesondere, wenn es im Laufe der Zeit Aktualisierungen gibt[3].\n\n\nZusammenfassung der inhaltlichen Anforderungen\n\n\n\n\n\n\nZusammengefasst sollte die Dokumentation alle W-Fragen beantworten\n\n\n\n\nWas tut die Software,\nwarum wurde sie geschrieben (wissenschaftlicher Zweck),\nwer soll sie nutzen,\nwie wird sie benutzt (Inputs, Outputs, Abläufe),\nwomit läuft sie (Umgebung/Abhängigkeiten),\nunter welchen Bedingungen (Annahmen/Limitationen) und\nwohin können sich Nutzer wenden (Support/Zitation).\n\n\n\nAll diese Punkte sorgen für Nachvollziehbarkeit (im Sinne von Reproduzierbarkeit der Ergebnisse) und Weiterverwendbarkeit (im Sinne von Adaptierbarkeit der Software für neue Kontexte).",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
- "objectID": "Writing/coding-age-ai.html#implementation",
- "href": "Writing/coding-age-ai.html#implementation",
- "title": "Field Report: Coding in the Age of AI with Cursor",
- "section": "Implementation",
- "text": "Implementation\nI only go briefly over the most important aspects of the implementation in Rules and their results. As this is also evolving with experience, there will be updates to those on the published place.\n\nThe Rules\nCursor rules are written in markdown with a small yaml-frontmatter. description is a string providing the agent with a description of the rule so it can be called like a tool (and basically injects the rule-content into the context). globs automatically inject the rule when the glob matches one attached filename. alwaysApply injects the rule into all contexts-windows.\n\nAlways apply rule: Basic info and behaviour\nThis tells the agent the project-structure and fixes some common pitfalls (i.e. the agent assuming bash, getting sidetracked, etc.).\n\n---\ndescription:\nglobs:\nalwaysApply: true\n---\n\n# IMPORTANT INFORMATION! DO NOT FORGET!\n\n## Conventions\n\n- Run tests with `poetry run pytest`.\n- **NO BASH IDIOMS IN COMMANDS**. Do not assume bash-usage. Use temporary\n scripts instead of bash-idoms like `<<` for text-input to a process.\n- Ask to update corresponding spec when you detect or implement a behaviour\n change.\n- Mention every time things are not implemented according to spec and offer to\n make them compliant.\n\n## Behavior of the Agent\n\n- If you have a task, ONLY do the task. Do not get distracted by failing tests,\n missing data etc. not related to your task.\n- Spec compliance is key. Check corresponding specs before you fix behaviour or\n ask the user if you should proceed.\n\n## Where to look for information?\n\n- Status & changes: `git status`, test failures in `tests/`\n- Active feature work: `/tasks/<feature>/TASKS.md`\n- Feature requirements: `/tasks/<feature>/PRD.md`\n- Feature specifications: `/specs/`\n- Source code: `/src/`\n- Tests & fixtures: `/tests/`\n- CLI entry point: `/src/<project>/cli/__main__.py`\n\n\n\nGet the PRD\n\n---\ndescription:\nglobs:\nalwaysApply: false\n---\n\n## Product Requirements Document (PRD)\n\n### Purpose\n\nDraft a concise, implementation‑ready Product Requirements Document (PRD) from a\none‑sentence feature description plus any additional Q&A with the stakeholder.\n\n### Output\n\n• Create /tasks/<feature>/PRD.md • Markdown only – no prose, no code‑fences. •\nFile structure: # <Feature title> ## 1. Problem / Motivation ## 2. Goals ## 3.\nNon‑Goals ## 4. Target Users & Personas ## 5. User Stories (Gherkin\n“Given/When/Then”) ## 6. Acceptance Criteria ## 7. Technical Notes /\nDependencies ## 8. Open Questions\n\n### Process\n\n1. Stakeholder provides a single‑sentence feature idea and invokes this rule.\n2. Look at specifications in `specs/` and inspect the code if needed to get an\n idea what the Stakeholder expects from this feature.\n3. Ask up to five clarifying questions (Q1 … Q5). If anything is still vague\n after five, look into the project with the new information provided. You may\n ask for further clarification up to 3 times following this schema, else flag\n it in _Open Questions_.\n4. After questions are answered reply exactly: Ready to generate the PRD.\n5. On a user message that contains only the word \"go\" (case‑insensitive): •\n Generate /tasks/<feature>/PRD.md following _Output_ spec. • Reply:\n <feature>/PRD.md created – review it.\n6. STOP. Do **not** generate tasks or code in this rule.\n\n### Writing guidelines\n\n• Keep each bullet ≤120 characters. • Use action verbs and measurable language.\n• Leave TBDs only in _Open Questions_. • No business fluff – pretend the reader\nis a junior developer.\n\n### Safety rails\n\n• Assume all work happens in a non‑production environment, unless otherwise\nstated or requested by you. • Do not include sensitive data or credentials in\nthe PRD. • Check the generated Document with `markdownlint` (if available),\napply auto-fixes and fix the remaining issues manually.\n\nA call to this rule usually looks like @generate-prd We noticed, that …. Therefore investigate the codebase to come up with a PRD addressing these issues..\n\n\nSpecifications\n\n---\ndescription: Specification Writing Guidelines\nglobs:\nalwaysApply: false\n---\n\n# Specification Writing Guidelines\n\n## Overview\n\nThis rule provides guidelines for writing and maintaining specifications in\n[specs/](mdc:specs) to ensure consistency, clarity, and prevent implementation\ndiscrepancies.\n\n## Specification Structure\n\n### Required Sections\n\nEvery specification should include:\n\n1. **Title and Purpose**\n\n ```markdown\n # Specification: [Component Name]\n\n Brief description of what this specification covers and its purpose.\n ```\n\n2. **Scope and Boundaries**\n\n - What is included/excluded\n - Dependencies on other specifications\n - Relationship to other components\n\n3. **Detailed Requirements**\n\n - Structured by logical sections\n - Clear, unambiguous language\n - Examples where helpful\n\n4. **Error Handling**\n\n - How errors should be handled\n - Fallback behaviors\n - Edge cases\n\n5. **Testing Requirements**\n - Expected test coverage\n - Snapshot requirements\n - Approval test criteria\n\n## Writing Standards\n\n### Clarity and Precision\n\n- **Use specific language**: Avoid vague terms like \"should\" or \"might\"\n- **Provide examples**: Include concrete examples for complex requirements\n- **Define terms**: Clearly define any technical terms or concepts\n- **Use consistent formatting**: Follow established patterns from existing specs\n\n### Structure and Organization\n\n- **Logical flow**: Organize sections in logical order\n- **Consistent headings**: Use consistent heading levels and naming\n- **Cross-references**: Link to related specifications using\n `[spec_name](mdc:specs/spec_name.md)`\n- **Code blocks**: Use appropriate language tags for code examples\n\n### Completeness\n\n- **Cover all cases**: Address normal, error, and edge cases\n- **Be exhaustive**: Don't assume implementation details\n- **Consider interactions**: How this spec relates to others\n- **Future-proof**: Consider potential changes and extensions\n\n## Specification Maintenance\n\n### Version Control\n\n- **Update specs first**: When changing behavior, update spec before\n implementation\n- **Document changes**: Use clear commit messages explaining spec changes\n- **Review process**: Have specs reviewed before implementation\n\n### Consistency Checks\n\n- **Cross-reference validation**: Ensure all links to other specs are valid\n- **Terminology consistency**: Use consistent terms across all specs\n- **Format consistency**: Follow established formatting patterns\n\n### Testing Integration\n\n- **Spec-driven tests**: Write tests based on specification requirements\n- **Snapshot validation**: Ensure snapshots match specification exactly\n- **Approval tests**: Use approval tests to catch spec violations\n\n## Quality Checklist\n\n### Before Finalizing Specification\n\n- [ ] All requirements clearly stated\n- [ ] Examples provided for complex requirements\n- [ ] Error cases covered\n- [ ] Cross-references to other specs included\n- [ ] Out of scope items clearly defined\n- [ ] Testing requirements specified\n- [ ] Consistent formatting throughout\n- [ ] Check the generated Document with `markdownlint` (if available), apply\n auto-fixes and fix the remaining issues manually.\n\n### Review Criteria\n\n- [ ] Is the specification unambiguous?\n- [ ] Are all edge cases covered?\n- [ ] Does it integrate well with other specs?\n- [ ] Is it testable?\n- [ ] Is it maintainable?\n\n## Common Pitfalls to Avoid\n\n### Ambiguity\n\n- **Vague language**: \"The system should handle errors gracefully\"\n- **Missing details**: Not specifying exact error handling behavior\n- **Unclear relationships**: Not explaining how components interact\n\n### Inconsistency\n\n- **Different terms**: Using different terms for the same concept\n- **Inconsistent formatting**: Not following established patterns\n- **Conflicting requirements**: Requirements that contradict other specs\n\n### Incompleteness\n\n- **Missing edge cases**: Not considering unusual scenarios\n- **Incomplete examples**: Examples that don't cover all cases\n- **Missing error handling**: Not specifying what happens when things go wrong\n\n## Related Rules\n\n- [spec-compliance-investigation.mdc](mdc:.cursor/rules/spec-compliance-investigation.mdc)\n How to investigate spec-implementation discrepancies\n- [base_overview.mdc](mdc:.cursor/rules/base_overview.mdc) Project structure and\n conventions\n\nAs it is obvious this is a very intricate rule with many criteria. For this you really need a reasoning and deep-thinking model that can also reason for extended times (many minutes are normal!) and call tools every now and then to get even more information. Models like o3, deepseek-r1 and the opus-series of claude really shine here.\n\n\nSpec Compliance\n\n---\ndescription: Spec Compliance Investigation Guide\nglobs:\nalwaysApply: false\n---\n\n# Spec Compliance Investigation Guide\n\n## Overview\n\nThis rule provides a systematic approach for investigating discrepancies between\nspecifications and implementations, following the pattern established. Do not\nchange any code during this phase.\n\n## Investigation Process\n\n### 1. Initial Analysis\n\n- **Locate specification**: Find the relevant spec file in [specs/](mdc:specs)\n- **Identify implementation**: Find corresponding source code in [src/](mdc:src)\n- **Check tests**: Review test files in [tests/](mdc:tests) for expected\n behavior\n- **Run tests**: Execute `poetry run pytest` to identify current failures\n\n### 2. Systematic Comparison\n\nFor each specification section:\n\n1. **Extract requirements** from spec file\n2. **Examine implementation** in source code\n3. **Compare outputs** with test snapshots\n4. **Document discrepancies** with specific examples\n\n### 3. Documentation Structure\n\nCreate analysis document in [tmp/spec\\_[component]\\_discrepancies.md](mdc:tmp/)\nwith:\n\n```markdown\n# [Component] Specification vs Implementation Discrepancies\n\n## Executive Summary\n\nBrief overview of findings and impact\n\n## Key Discrepancies Found\n\n### 1. [Category] - [Specific Issue]\n\n**Specification:**\n\n- Requirement details\n\n**Implementation:**\n\n- Current behavior\n- ✅ Correct aspects\n- ❌ Incorrect aspects\n\n## Test Results\n\n- Current test failures\n- Output differences\n\n## Impact Assessment\n\n### High Impact Issues:\n\n- Critical functionality problems\n- User experience issues\n\n### Medium Impact Issues:\n\n- Consistency problems\n- Formatting issues\n\n### Low Impact Issues:\n\n- Minor differences\n- Style variations\n\n## Recommendations\n\n### Option 1: Update Spec to Follow Code\n\n**What to change:**\n\n- Specific spec modifications\n\n**Pros:**\n\n- Benefits of this approach\n\n**Cons:**\n\n- Drawbacks of this approach\n\n### Option 2: Update Code to Follow Spec\n\n**What to change:**\n\n- Specific code modifications\n\n**Pros:**\n\n- Benefits of this approach\n\n**Cons:**\n\n- Drawbacks of this approach\n\n### Option 3: Recommended Hybrid Approach\n\n**Recommended Solution:**\n\n- Phased implementation plan\n\n**Rationale:**\n\n- Why this approach is best\n\n**Implementation Priority:**\n\n- Immediate, short-term, medium-term tasks\n```\n\n## Quality Checklist\n\n### Before Finalizing Investigation\n\n- [ ] All specification sections reviewed\n- [ ] Implementation code thoroughly examined\n- [ ] Tests run and failures documented\n- [ ] Impact assessment completed\n- [ ] All three solution options evaluated\n- [ ] Recommendation justified with rationale\n- [ ] Implementation plan prioritized\n\n### Documentation Quality\n\n- [ ] Specific examples provided for each discrepancy\n- [ ] Code snippets included where relevant\n- [ ] Pros/cons clearly articulated\n- [ ] Implementation steps detailed\n- [ ] Priority levels assigned\n\n## Related Rules\n\n- [Base Project Overview](mdc:.cursor/rules/base-project-overview.mdc) Project\n structure and conventions\n- [Spec Guidelines](mdc:.cursor/rules/spec-guidelines.mdc) How to write\n specifications\n\nThis compliance-report also need a deep-thinking model, like the specification beforehand.\n\n\nTasks\n\n---\ndescription:\nglobs:\nalwaysApply: false\n---\n\n## Task List creation\n\n### Purpose\n\nTranslate a Product Requirements Document (PRD) into an executable Markdown task\nlist that a junior developer (human or AI) can follow without extra context.\n\n### Output\n\n• Create /tasks/<feature>/TASKS.md (overwrite if it exists). • Markdown only, no\nprose around it. • Epics = H2 headings (`## 1. <Epic>`). • Tasks = unchecked\ncheck‑boxes (`- [ ] 1.1 <task>`). • Sub‑tasks = indent one space under their\nparent (` - [ ] 1.1.1 <subtask>`). • Create a\n/tasks/<feature>/Task*<Epic>*<task>\\_<subtask>.md (i.e. `Task_3_2_4.md` for Epic\n3, Task 2, Subtask 4)\n\n### Process\n\n1. Read the tagged PRD.\n2. **Investigate** the current state of the repository to collect answers to\n your first questions. All specs for fixed behaviours and outputs are located\n in `specs/`. **Consult those** as a source first before trying to\n reverse-engineer from the code. \n If specs are in need of change then this is also a task to be generated.\n3. If critical info is missing and cannot be answered by looking at the code,\n ask max five clarifying questions (Q1 … Q5) and stop until answered.\n4. After questions are answered think about the answers and: Either: look at the\n code again, then goto 3., and ask for further clarification Or: Reply\n exactly: Ready to generate the subtasks – respond **go** to proceed.\n5. On a user message that contains only the word \"go\" (case‑insensitive): a.\n Generate /tasks/<feature>/TASKS.md following _Output_ spec. b. Reply with:\n TASKS.md created – review them.\n6. After TASKS.md was reviewed, create `Task_<e>_<t>_<s>.md` for each task and\n subtask containing implementation hints like relevant specs (link them!),\n primary files to edit/review for this task, tests needing change, etc.\n7. Stop. Do **not** begin executing tasks in this rule.\n\n### Writing guidelines\n\n• Each item ≤120 characters, start with an action verb. • Hints are allowed\nbelow each item as HTML-Comment and do not count against the 120 characters. •\nGroup related work into logical epics with ≤7 direct child items. • Prefer\nconcrete file paths, commands, specs or APIs when available. • Skip\nimplementation details obvious from the codebase in the overview. • If a task\nonly concerns up to 5 files, name them in the detailed file. Otherwise give\nhints on how to search for them (i.e. \"everything under src/models/\").\n\n### Safety rails\n\n• Never touch production data. • Assume all work happens in a feature branch,\nnever commit directly to main. • Check the generated Document with\n`markdownlint` (if available), apply auto-fixes and fix the remaining issues\nmanually.\n\nThis also works better with one of those deep-thinking models.\n\n\nOther Rules\nI have some other rules with guidelines on how to write proper tests, one for “just follow the tasks in TASKS.md one by one until done with commit after each task”, etc. Those are omitted for brevity. Also they are so simple, that non-reasoning-models can follow them. Cheap claude3-sonnet or small, specialised coding-LLMs are enough to get the job done with this preparation.",
+ "objectID": "Writing/documentation.html#format-und-struktur-der-dokumentation",
+ "href": "Writing/documentation.html#format-und-struktur-der-dokumentation",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Format und Struktur der Dokumentation",
+ "text": "Format und Struktur der Dokumentation\nFür Forschende ohne viel Ressourcen muss die Dokumentation einfach zugänglich, leicht pflegbar und ohne Spezialsoftware erstellbar sein.\n\nREADME.md als zentrales Dokument\nDie Hauptdokumentation sollte als README in Markdown-Format im Hauptverzeichnis des Code-Repositoriums liegen. Dieses README fungiert als “Startseite” des Projekts und enthält idealerweise eine komprimierte Übersicht aller wichtigen Punkte:\n\nZweck der Software,\nKurzbeschreibung,\nInstallation,\nkurzer Nutzungsbeispiel,\nKontakt/Lizenz.\n\nAuf Plattformen wie GitHub, GitLab etc. wird die README automatisch angezeigt, was die Sichtbarkeit erhöht. Die Vorteile von Markdown sind die einfache Lesbarkeit in Rohform, die breite Unterstützung (auch in Renderern wie GitHub-Webansicht) und die Eignung für Versionierung (Textdatei im git). So bleibt die Dokumentation eng mit dem Code verzahnt und unter Versionskontrolle – denn Dokumentation soll statisch und zusammen mit den Daten/Code abgelegt werden[3].\n\n\nKeine proprietären Formate oder Abhängigkeit von Werkzeugen\n\n\n\n\n\n\n\nPrinzip\n\n\n\nDokumentation gehört zum Code und muss auch ohne Programm lesbar sein.\n\nUm Hürden für die Erstellung und Nutzung der Dokumentation gering zu halten, sollte auf gängige, offene Formate gesetzt werden (Plaintext, Markdown, reStructuredText).\nVermeiden Sie nach Möglichkeit Formate wie Word-Dokumente oder PDF als primäre Dokumentationsquelle – solche Formate sind nicht diff-freundlich, erschweren Zusammenarbeits-Workflows und sind meist nicht Teil des Versionskontrollsystems. Ein Markdown-Dokument hingegen kann gemeinsam mit dem Code gepflegt werden, und Änderungen sind transparent nachvollziehbar.\nIm Sinne der Digital Longevity[3] ist eine statische HTML- oder PDF-Version der Dokumentation (automatisch generiert aus Markdown via pandoc) als Teil der Release-Artefakte sinnvoll. Wichtig ist aber, dass die Quelle der Wahrheit immer die im Repository gepflegte Doku bleibt.\n\n\nStrukturierte Unterteilung in weitere Dateien/Abschnitte\n\n\nexample-project/\n├── README.md\n├── CONTRIBUTING.md (optional)\n├── CHANGELOG.md (optional)\n├── CITATION.md (oder CITATION.cff)\n├── LICENSE\n├── data/ (optional)\n│ └── sample_data.csv\n├── docs/ (optional)\n│ ├── INSTALL.md\n│ └── USAGE.md\n├── examples/ (optional)\n│ └── example_workflow.ipynb\n└── src/\n ├── script.py\n └── module/\n └── helper.py\nBeispielhafter Struktur eines Code-Repositories\nSollte die Dokumentation umfangreicher sein, ist es sinnvoll, sie in logisch getrennte Abschnitte aufzuteilen. Dies kann innerhalb der README durch Überschriften geschehen oder durch zusätzliche Markdown-Dateien im Repository (z. B. eine INSTALL.md für ausführliche Installationshinweise, eine USAGE.md oder TUTORIAL.md für detaillierte Benutzeranleitungen, eine CHANGELOG.md für Changelog etc.). Eine gängige Struktur ist z. B.:\n\nREADME.md – Überblick (Ziel, Installation, kurzes Beispiel, Lizenz/Zitation)\ndocs/ Verzeichnis mit weiteren .md-Dateien für tiefergehende Dokumentation (optional)\nCONTRIBUTING.md – Hinweise für Beiträger (falls relevant)\nLICENSE – Lizenztext\nCITATION.cff oder CITATION.md – wie zu zitieren.\n\nDiese Dateien sollten konsistent formatiert und wie oben benannt sein, damit sie leicht auffindbar und ggf. direkt durch Tools verarbeitbar sind.\n\n\nÜbersichtlichkeit und Navigierbarkeit\n\n\n\n\n\n\n\nPrinzip\n\n\n\n“Don’t Repeat Yourself”: Alle Informationen zu einem Thema (Installation/Nutzung/…) an derselben Stelle sammeln und keinesfalls mehrfach pflegen.\n\nStrukturieren Sie die Dokumentation mit klaren Überschriften und Listen, damit Leser schnell die gesuchten Informationen finden. Eine logische Gliederung hilft unterschiedlichen Nutzergruppen gezielt das Relevante zu finden. Für längere Dokumente kann ein Inhaltsverzeichnis oder eine Abschnittsübersicht am Anfang nützlich sein. Markdown bietet z. B. automatische TOC-Generierung auf manchen Plattformen.\nAchten Sie darauf, pro Abschnitt nur zusammenhängende Informationen zu behandeln (z. B. alles zu Installation an einem Ort) und Wiederholungen zu vermeiden. Das Mantra “Don’t Repeat Yourself” gilt auch für Dokumentation.\n\n\nBeispiele, Codeblöcke und ggf. Abbildungen einbinden\n\n\n\n\n\n\n\nPrinzip\n\n\n\nZeigen statt nur beschreiben – konkrete Anwendungsfälle in der Doku verankern.\n\nNutzen Sie die Möglichkeiten von Markdown, um die Dokumentation lebendig zu gestalten. Zeigen Sie Code-Beispiele als formatierte Codeblöcke, fügen Sie Links zu weiterführenden Ressourcen ein, oder binden Sie bei Bedarf Abbildungen ein (etwa ein Diagramm der Datenpipeline, ein Screenshot der Benutzeroberfläche, etc.).\nAchten Sie dabei auf Dateigrößen und Formate (Bilder als PNG/JPG, Diagramme wenn möglich als SVG für Langlebigkeit). Falls Diagramme der Architektur oder Workflow-Abbildungen hilfreich sind, können diese mit simplen Mitteln erstellt werden3.\n3 zur Not handgezeichnet und abfotografiert, besser jedoch mit Tools wie mermaid.js Diagrammen in Markdown oder graphvizDiese Visualisierungen sind jedoch nur dann einzusetzen, wenn sie echten Mehrwert bieten und ohne komplexe Build-Prozesse eingebunden werden können. Im Zweifel hat textuelle Beschreibung Vorrang, um nicht vom Prinzip “keep it simple” abzuweichen.\n\n\n\n\n\n\nTypische Nutzungsszenarien und Workflows\n\n\n\n\n\nZeigen Sie anhand von konkreten Beispielen, wie die Software benutzt wird. Ein Quickstart-Beispiel senkt die Einstiegshürde enorm. Dies kann z. B. eine Anleitung sein, wie man mit wenigen Schritten von einer Eingabedatei zum gewünschten Ergebnis kommt.\nBeschreiben Sie typische Workflows in nachvollziehbaren Schritten: Eingabe vorbereiten, Software-Befehl/GUI-Aktion ausführen, Ausgabe interpretieren. Ggf. können mehrere Anwendungsfälle skizziert werden (z. B. “Analyse eines einzelnen Briefes” vs. “Batch-Verarbeitung eines gesamten Korpus”).\nDiese Beispiele sollten realistisch und möglichst repräsentativ für wissenschaftliche Anwendungen sein. Nutzen Sie gerne kleine, mitgelieferte Datensamples oder Defaults, damit Nutzer die Beispielschritte direkt ausprobieren können. Idealerweise werden Code-Beispiele mit ausgegebenen Resultaten gezeigt (z. B. in Form von Ausschnitten oder, bei Kommandozeilentools, via --help dokumentiert).\n\n\n\n\n\nUmfang und Fokus der Dokumentation\n\n\n\n\n\n\n\nPrinzip\n\n\n\nKann eine neue Person in < 1 Stunde mit Hilfe der Doku das Tool zum Laufen bringen und ein einfaches Beispiel ausführen?\n\nWenn ja, ist der Detailgrad angemessen\nWenn die Person hingegen nach 10 Seiten oder mehr als 1 Stunde immer noch nicht weiß, wie sie loslegen soll, muss die Doku fokussierter werden.\n\n\nEin effizienter Umfang lässt sich erreichen, indem sie alles, was für Nachvollziehbarkeit und Wiederverwendung nötig ist dokumentieren, und alles andere skippen.\n\nNegativbeispiele umfassen:\n\njeder interne Programmiertrick wird erläutert – Quellcode-Kommentare richten sich an Entwickler*innen, während die Nutzendendokumentation sich auf Nutzung und Kontext beschränkt\nseitenlange Theorieabhandlungen (verweisen Sie stattdessen auf Papers)\ngenerische Erklärungen bekannter Technologien (man muss Git oder Python nicht in der Doku erklären, sondern kann referenzieren)\n\n\nHalten Sie auch die Sprache prägnant:\n\nkurze Absätze\nListen\nund einfache Sätze\n\nerhöhen die Lesbarkeit.\nFachtermini aus dem jeweiligen wissenschaftlichen Bereich dürfen verwendet werden, aber erklären/verlinken Sie sie, falls die Zielnutzer sie evtl. nicht kennen.\n\nFokus auf Nutzer*innen - nicht Entwickler*innen\nStellen Sie sich beim Schreiben der Doku die verschiedenen Nutzerrollen vor: “Zukünftiges Ich”, Kolleg*innen, Fachforscher*innen anderer Disziplin und ggf. Software-Entwickler*innen, die den Code erweitern. Jede dieser Gruppen möchte bestimmte Dinge wissen.\nForschende fragen:\n\nWas kann das Tool?\nWie benutze ich es?\nIn welchem Kontext steht es?\n\nEntwicklende Personen fragen:\n\nWie kann ich beitragen?\nWie funktioniert es unter der Haube?\n\nPriorisieren Sie zunächst die erstgenannten (Anwender) – deshalb Fokus auf Zweck, Nutzung und Ergebnisse. Konzentrieren Sie die Hauptdokumentation darauf, das Nutzen und Verstehen der Software von außen zu ermöglichen.\n\n\n\nPriorisierung bei Zeitmangel\nDieser Katalog adressiert primär die Nutzerdokumentation (für Endnutzer und für die Autoren selbst, wenn sie das Tool später wieder anfassen). Entwickler*innendokumentation (z. B. detaillierte API-Dokumente, Code-Kommentare, technische Architektur) kann separat gehalten werden, oder sogar automatisch erzeugt werden.\n\nMinimaldokumentation: kurze Kommentare\nBeginnen Sie mit einer Minimaldokumentation, die alle Schlüsselaspekte abdeckt (“keine Dokumentation” ist keine Option). Good Enough Practices[5] empfehlen, als ersten Schritt zumindest einen kurzen erklärenden Kommentar am Anfang jedes Scripts oder eine README mit ein paar Sätzen zu erstellen. Diese Hürde ist niedrig und bringt bereits Nutzen – selbst wenn (noch) keine ausführliche Handbuch-Doku existiert. Später kann die Dokumentation erweitert werden, insbesondere wenn die Software in Kooperation entsteht oder mehr Nutzer gewinnt. Es hat sich gezeigt, dass ausführliche Dokumentation oft erst entsteht, wenn ein echter Bedarf (z. B. durch externe Nutzer) vorhanden ist. Daher: zögern Sie nicht, zunächst klein anzufangen, aber stellen Sie sicher, dass zumindest die kritischen Informationen sofort verfügbar sind (lieber ein 2-seitiges README heute, als das perfekte 30-seitige Handbuch in zwei Jahren, das evtl. nie geschrieben wird).\n\n\nVerlinkte Dokumentation ist auch Dokumentation\nNutzen Sie Verweise und vorhandene Ressourcen. Wenn z. B. Ihr Tool auf einem komplizierten Setup (Datenbank, Webserver) aufbaut, brauchen Sie nicht jede Installationsoption im Detail in Ihrer Doku zu reproduzieren – verlinken Sie auf offizielle Installationsanleitungen dieser Abhängigkeiten, und nennen Sie nur Ihre spezifischen Konfigurationen und verlinken sie auf die Dokumentation des Setup-Elementes für alles weitere. Ebenso können Tutorials oder Papers, die schon existieren, als weiterführende Links angegeben werden, anstatt Inhalte redundant zu erklären. Das entlastet Ihre Dokumentation und hält sie schlank.\n\n\nUnd anschließend?\nWenn der Zeitmangel vorüber ist4, sollte man nach und nach das folgende Kapitel umsetzen.\n4 als ob DAS je der Fall wäre -.-",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
- "objectID": "Writing/coding-age-ai.html#example-rules-in-action",
- "href": "Writing/coding-age-ai.html#example-rules-in-action",
- "title": "Field Report: Coding in the Age of AI with Cursor",
- "section": "Example: Rules in Action",
- "text": "Example: Rules in Action\nThe codebase we look at here is a project called gitlab_overviewer. It takes gitlab-api-keys and generates nice overviews for tracking metadata in different projects across different groups. With a nice export to markdown (for rendering in gitlab itself) and quarto (for exporting to i.e. confluence) with multiple pages etc. pp.\nThe current issue is, that due to a complete rewrite we are happy with the current output, but there are some minor problems still remaining. Some of them may also due to specs being incorrect, because requirements or ideas changed over time.\n\nAnalyzing the situation\nFor the analysis o3 was used and it was sufficient on first try. Here is (part of) the conversation:\n\n\n\n\n\n\nUser\n\n\n\n@spec-compliance-investigation.mdc do a spec-compliance investigation on @spec_renderer_markdown.md . The relevant modules in the @/src should link to the spec (omissions are possible and also out-of-spec).\n\n\n\n\n\nThe initial analysis with most thinking steps opened. It is interesting to see the agent collect needed information onthe first try and generates a lot of ‘thought’.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\ntmp/spec_renderer_markdown_discrepancies.md:\n\nMarkdown Renderer Specification vs Implementation Discrepancies\nExecutive Summary\nThe current render_grouped_markdown implementation provides most of the layout and structural requirements defined in specs/spec_renderer_markdown.md. However, several key aspects diverge from the specification, leading to failing approval tests and user-visible issues. The most critical problems concern ordering guarantees and the content chosen for the Description callout.\nKey Discrepancies Found\n1. Ordering – Projects inside detailed sections\nSpecification:\n\nProjects in the detailed part “appear in the same order they had inside their group’s summary table” (Spec §4, bullet 3).\n\nImplementation:\n# ... excerpt ...\nfor od in sorted(group_projects, key=lambda p: p.project.name.lower()):\n # renders project sections alphabetically\n\nProjects are sorted alphabetically, not in the original summary-table order.\n✅ Groups themselves are ordered by descending project count (matches spec).\n❌ Project order within groups breaks the “mirror the incoming, already-sorted data” rule.\n\n2. Description callout picks raw front-matter if no description key exists\nSpecification:\n\nIf front-matter contains a description key → use its value (callout-type tip).\nElse if Readme.content is not empty → use that content (callout-type warning).\nReadme.content is defined as the first real paragraph without front-matter (see Model spec).\n\nImplementation:\n\nCorrectly checks for .raw_frontmatter[\"description\"] first ⬆️.\nThen falls back to od.readme.content unconditionally. In practice many fixture READMEs still include --- … YAML because the pre-processing step did not strip it, so the callout now contains the entire front-matter block – cf. failing snapshot diff lines starting with > --- \"author\":.\n✅ Callout types (tip/warning/danger) are chosen per spec.\n❌ Content for the warning variant violates the “no raw front-matter” expectation and visual quality.\n\n8< ……… 8<\nTest Results\npoetry run pytest -q → 5 failures / 119 passed\n • tests/rendering/test_markdown_renderer_approval.py (output mismatch)\n • tests/rendering/test_quarto_renderer_approval.py (cascade – shares ordering logic)\n • tests/models/test_model_invariants.py (3 failures unrelated to renderer)\nThe Markdown approval diff highlights exactly the two major discrepancies above (alphabetical ordering & raw front-matter in callout).\nImpact Assessment\nHigh Impact Issues\n\nIncorrect project ordering – breaks reproducibility guarantees and user expectations; also propagates to Quarto renderer.\nRaw YAML in description callout – severely harms readability of generated documentation.\n\nMedium Impact Issues\n\nNone identified besides cosmetic phrasing.\n\nLow Impact Issues\n\nMinor stylistic deviations (e.g., spacing) appear compliant within tolerance.\n\nRecommendations\nOption 1: Update Spec to Follow Code\nWhat to change:\n\nRelax ordering requirement to alphabetical order.\nAllow raw front-matter YAML in description callout.\n\nPros: No code changes; quick test green.\nCons: Violates original design intent; exposes low-level data to end-users; poor UX.\nOption 2: Update Code to Follow Spec (preferred)\nWhat to change:\n\nRemove the sorted(...) call to preserve input ordering.\nEnsure Readme.content truly excludes YAML front-matter (use parse_readme during data ingestion or strip before rendering).\n\nPros: Restores spec fidelity; fixes approval tests; better documentation quality.\nCons: Requires code edit & possibly data-loading adjustments.\nOption 3: Hybrid Approach\n\nFix ordering in renderer.\nKeep current fallback but truncate front-matter to first paragraph until improved extraction available.\n\nRecommended Solution: Option 2 – small, well-scoped patches bring implementation back in line with spec and tests.\nImplementation Priority:\n\nImmediate: ordering fix & front-matter stripping.\nShort-term: add regression tests for ordering.\nMedium-term: audit other renderers (Quarto) for same issues.\n\n\nI have completed the investigation phase and produced a detailed report at\ntmp/spec_renderer_markdown_discrepancies.md\nKey take-aways:\n• Two core deviations from the Markdown-renderer spec were found\n\nProject ordering in the detailed section is alphabetical instead of mirroring the summary order.\nWhen a README lacks a description: key, the Description call-out falls back to raw front-matter, so YAML blocks are rendered verbatim.\n\n• These issues are the root cause of the failing approval tests.\n• A concise impact assessment and three solution options are documented; the recommended path is to fix the code to match the specification.\nLet me know if you’d like to proceed with the recommended fixes or explore the alternate options.\n\n\n\n\n\nCorrecting the analysis\nBy reading the analysis i soon noticed, that things were missing out of the spec. So i added a bit of text:\n\n\n\n\n\n\nCommenting the result\n\n\n\n\n1. Ordering – Projects inside detailed sections\nSpecification: > […snip…]\n\n❌ Project order within groups breaks the “mirror the incoming, already-sorted data” rule.\n\nComment:\n\nThe implemented behaviour is using alphabetical order in the detailed section and in the table according to the sort defined in the @spec_table_config . The spec needs updating.\n\n\n\n2. Description callout picks raw front-matter if no description key exists\nSpecification: > […snip…]\n\n❌ Content for the warning variant violates the “no raw front-matter” expectation and visual quality.\n\nComment:\n\nThis is a violation of spec. But additionally it should only render the first 10 lines of Readme.content and then add a …continues… if there are more lines.\n\n\n\n\nArmed with those correction i told the LLM to generate better recommendations, that align with my goals. The LLM then deleted the whole recommendations-section and wrote exactly what i wanted. Again o3 was used for the slow and deep thinking.\n\n\n\n\n\n\nUser\n\n\n\nI added comments to the part that are not up to spec. Update the recommendations in @spec_renderer_markdown_discrepancies.md according to those comments.\n\n\n\n\n\nThoughts of the agent after i updated the comments. It always feels good to read that i was understood3 by the machine.\n3 yaya .. i know. Antropomorphing an AI.\n\n\n\n\n\n\n\n\nCursor\n\n\n\ntmp/spec_renderer_markdown_discrepancies.md:\n\n// … existing code …\nRecommendations\nOption A: Align Specification with Current Ordering (preferred)\nWhat to change:\n\nAmend spec_renderer_markdown.md §4 to state that projects in the detailed section appear alphabetically by project name. This legitimises the current implementation and removes the ordering-related approval failure.\nKeep the existing implementation for project ordering unchanged.\nChange the renderer implementation for the Description call-out:\n\nStrip YAML front-matter from Readme.content (use parse_readme).\nRender at most the first 10 non-empty lines of the resulting text.\nIf more lines remain, append a marker …continues… as the final line inside the call-out.\n\nAdd corresponding wording to the spec clarifying the 10-line truncation rule.\n\nPros:\n\nLeast intrusive to working behaviour users already rely on.\nFixes readability issue without reordering content.\nSmaller diff in test fixtures (only call-out content).\n\nCons:\n\nRequires a spec update (but that is a documentation-only task).\n\nOption B: Keep Original Spec Ordering, Change Code\nWhat to change:\n\nRemove sorted() so project order mirrors summary-table order.\nApply the same Description call-out improvements as in Option A.\n\nPros:\n\nUpholds original design intent of summary/detailed symmetry.\n\nCons:\n\nGenerates a larger diff for existing artefacts; may surprise current users.\n\nRecommended Solution\nProceed with Option A:\n\nUpdate the specification paragraph on ordering.\nImplement the 10-line truncation + …continues… logic in the renderer.\n\nImplementation Priority\n\nImmediate: implement call-out truncation logic & adjust approval snapshots.\nImmediate: update Markdown renderer spec for alphabetical ordering and call-out truncation.\nShort-term: add regression tests for the new truncation rule.\nMedium-term: propagate the truncation rule to the Quarto renderer to keep output parity. // … existing code …\n\n\nThe “Recommendations” section has been rewritten to reflect your comments:\n1. Ordering: keep the current alphabetical behaviour and adjust the spec instead.\n2. Description call-out: implement a 10-line preview with a “…continues…” marker after stripping front-matter.\nThese points are now captured as Option A (preferred), with a revised implementation roadmap.\nLet me know when you’d like me to start on the code/spec changes.\n\n\n\n\n\nCreating Tasks\nNow that we have a clear plan without touching any code, lets flesh out the Details.\n\n\n\n\n\n\nUser\n\n\n\n@generate-tasks.mdc generate Tasks for Option A of @spec_renderer_markdown_discrepancies.md under a feature called “markdown_rendering”.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\nTASKS.md:\n\n1. Spec updates\n\n1.1 Amend alphabetical ordering rule in specs/spec_renderer_markdown.md\n1.2 Document 10-line truncation rule for Description call-out\n\n2. Markdown renderer implementation\n\n2.1 Strip YAML front-matter before rendering description\n\n2.1.1 Invoke parse_readme to obtain cleaned content\n2.1.2 Add unit test ensuring YAML is absent in call-out\n\n2.2 Truncate description to first 10 lines with …continues…\n\n2.2.1 Implement truncation logic in render_grouped_markdown\n2.2.2 Update approval snapshot for Overview.md\n\n2.3 Verify alphabetical ordering comment references spec\n\n3. Quarto renderer alignment\n\n3.1 Port truncation & YAML-stripping logic to src/gitlab_overviewer/rendering/quarto.py\n3.2 Refresh Quarto renderer snapshot & tests\n\n4. Tests & snapshots\n\n4.1 Update Markdown renderer approval snapshot after changes\n4.2 Update Quarto renderer snapshot after changes\n4.3 Add regression test for 10-line truncation rule\n\n5. Documentation & housekeeping\n\n5.1 Run markdownlint and fix issues in updated docs/specs\n5.2 Add entry to CHANGELOG.md under Unreleased\n\n\nTask_1_1.md:\n\nTask 1.1 – Amend alphabetical ordering rule in spec_renderer_markdown.md\nRelevant spec: specs/spec_renderer_markdown.md (§4 Ordering Guarantees)\nGoal: Replace the bullet “projects appear in the same order they had inside their group’s summary table” with “projects appear alphabetically by their name”.\nPrimary files:\n\nspecs/spec_renderer_markdown.md\n\nHints:\n\nSearch for the existing ordering clause (§4 bullet 3) and update accordingly.\nRun markdownlint after editing: npx markdownlint-cli2 specs/spec_renderer_markdown.md --fix.\n\n\nTask_1_2.md:\n\nTask 1.2 – Document 10-line truncation rule for Description call-out\nRelevant spec: specs/spec_renderer_markdown.md (§3 Project Detail Sections)\nGoal: Add a subsection clarifying that when the Description call-out uses Readme.content, the renderer must:\n\nStrip YAML front-matter.\nRender the first 10 non-empty lines.\nAppend …continues… if more content was omitted.\n\nPrimary files:\n\nspecs/spec_renderer_markdown.md\n\nHints:\n\nPlace this clarification right after rule 3.1 in §3.\nProvide a small Markdown example block.\nRemember to update any cross-links.\n\n\n[…further tasks omitted for brevity…]\n\n\n\n\n\nExecuting\nWith all that detailed instructions changes to the code are narrowly focused and the changes are easily reviewable. Execution does not need any deep thinking and can be done by a fast and cheap code-model like gemini-2.5, claude3-sonnet, etc.\nThey usually just loads all relevant files as context, work step-by-step, verify, tick off the task and report back for review.\n\n\n\n\n\n\nUser\n\n\n\n@xxx-work-on-tasks.mdc @/markdown_rendering Implement all Task1\\*.md.\n\n\n\n\n\n\n\n\n\nCursor\n\n\n\nI’ll help you implement the tasks from Task1*.md in the markdown rendering feature. Let me first check what these tasks entail.\nLet me first check the current state of the spec file we need to modify.\nI’ll now implement both tasks. Let’s start with Task 1.1 - amending the alphabetical ordering rule.\n// ... existing code ...\n\n## 4. Ordering Guarantees\n\n- Groups are ordered by the number of Projects rendered (alphabetically on a\n tie).\n- Summary project order mirror the incoming, already-sorted data (see\n [Table Sorting](./spec_table_sorting.md)).\n- In the detailed part, projects appear alphabetically by their name.\n\n// ... existing code ...\nNow let’s implement Task 1.2 - documenting the 10-line truncation rule for the Description call-out.\n// ... existing code ...\n\nMissing entries from the Project-Info are omitted.\n\nWhen using `Readme.content` in a Description call-out, the renderer must:\n\n```markdown\n1. Strip any YAML front-matter from the content\n2. Include only the first 10 non-empty lines\n3. If content was truncated, append \"…continues…\" on a new line\n```\n\nExample:\n\n```markdown\n> [!warning] Description\n>\n> # My Project\n>\n> This is a great project that does many things. It has multiple features:\n>\n> - Feature 1\n> - Feature 2\n> - Feature 3\n> - Feature 4 …continues…\n```\n\n2. **Release badge** – image linking to `<web_url>/-/releases`. // ...\n existing code ...\nLet me run markdownlint as suggested in the task hints to ensure our changes meet the markdown style guidelines.\nI’ve completed both tasks:\n\nTask 1.1: Updated the ordering rule in section 4 to specify that projects appear alphabetically by their name in the detailed part, replacing the previous rule about maintaining summary table order.\nTask 1.2: Added documentation about the Description call-out truncation rules after section 3.1, including:\n\nThe three required steps (strip YAML front-matter, render first 10 non-empty lines, append “…continues…”)\nA clear example showing how the truncation should look\n\n\nThe markdownlint output shows some line length issues and a heading issue, but these appear to be pre-existing in the file and not related to our changes. The changes we made maintain the existing formatting style of the document.\nWould you like me to address any of the markdownlint issues, or would you prefer to keep the changes focused on just implementing the two tasks?\n\n\n\nAnd within 1 Minute everything was updated as expected and verified within a few seconds. Here you can really see the advantages of this approach. Instead of going off-track and focusing i.e. on fixing the markdownlint-issues4 the agent notes that this is still an issue, but offers to keep focused on the tasks for the moment. With a simple ‘go’ i can continue tackle the next thing. 5 Minutes later everything is done and we have one less issue.\n4 As i have often expirienced with LLMs trying to be too perfect and pleasing the user without ever stopping",
+ "objectID": "Writing/documentation.html#was-macht-eine-gute-dokumentation-aus",
+ "href": "Writing/documentation.html#was-macht-eine-gute-dokumentation-aus",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Was macht eine gute Dokumentation aus",
+ "text": "Was macht eine gute Dokumentation aus\n\nFormelle Prinzipien: Open-Source-Research, FAIR4RS und ENDINGS\nBeachten Sie, dass dieser Anforderungskatalog in Einklang mit den Prinzipien des Research Software Engineering[1] und den FAIR4RS-[4] bzw. ENDINGS-Prinzipien[3] steht.\n\n\n\n\n\n\n\nFAIR4RS-Prinzipien für Software\n\n\n\nDie FAIR4RS-Prinzipien sind eine Anpassung der Ursprünglich nur für Daten gedachten FAIR-Prinzipien. Der Fokus liegt hier nicht auf Software selbst, sondern auf eine Nutzung von Software die ein Äquivalent zur Nutzung von FAIR-Daten darstellt.\n\n\n\n\n\n\n\nENDINGS-Prinzipien\n\n\n\nDie ENDINGS-Prinzipien für digitale Projekte betonen insbesondere die Bedeutung von Dokumentation für Datenstrukturen, offenen Lizenzen, statischen Outputs und Zitierbarkeit.\n\n\nGute Dokumentation bedeutet daher u.a. die Verdeutlichung und Sicherstellung von\n\nReproduzierbarkeit (Installation, Daten, Beispiele),\nOffenheit (Lizenz, offene Formate) und\nNachhaltigkeit (Versionierung, Langlebigkeit der Doku).\n\nIndem Sie also diesem Anforderungskatalog folgen, berücksichtigen Sie automatisch wichtige anerkannte Prinzipien für gute wissenschaftliche Softwarepraxis.\n\n\nNutzungshilfen außerhalb der Dokumentation\nFalls Ihre Software ein Command-Line Interface (CLI) hat, stellen Sie sicher, dass eine eingebaute Hilfe vorhanden ist (z. B. Ausgabe bei --help). Viele Nutzer greifen zunächst darauf zurück. Dieses Hilfemenü sollte kurz erläutern, welche Subkommandos oder Optionen existieren. Moderne CLI-Frameworks generieren solche Hilfen oft automatisch aus Ihrem Code (z. B. argparse in Python erzeugen --help-Texte). Nutzen Sie das, um konsistente Infos zu garantieren.\nFür GUI-Anwendungen sollten Tooltips, Hilfetexte in der Oberfläche oder zumindest ein kleiner Help-Abschnitt im Handbuch vorhanden sein. Diese eingebetteten Hilfen ersetzen keine ausführliche Dokumentation, aber sie senken die Schwelle für alltägliche Fragen.\n\n\nKontinuierliche Verbesserung und Feedback\nDokumentation ist kein einmaliges Ereignis, sondern ein fortlaufender Prozess. Best Practice sind daher insbesondere:\n\nfrüh Feedback von Testnutzer*innen oder Kolleg*innen einzuholen: Lassen Sie jemanden die Anleitung befolgen und hören Sie auf Stolpersteine. Oft zeigen sich Lücken erst im Praxistest (“Ich wusste nicht, was ich nach Schritt X tun soll” etc.).\nPlanen Sie Zeiten ein, die Dokumentation nachzuführen, insbesondere wenn sich die Software ändert. Ein lebendiges Projekt wird vielleicht Release für Release die Dokumentation erweitern (evtl. neue Tutorials, neue Module dokumentieren). Spätestens zum Release-Zeitpunkt sollten diese auffallen und ggf. als Issues adressiert werden.\nNutzen Sie auch Issues für Dokumentation: Wenn Nutzer Fragen stellen, überlegen Sie, ob die Antwort in die offizielle Doku übernommen werden sollte. So wächst die Dokumentation organisch entlang der tatsächlichen Bedürfnisse.\n\n\n\nPositiv- und Negativbeispiele studieren\nSchlussendlich ist ein guter Weg, die eigene Dokumentation zu verbessern, ist ein Blick auf Projekte mit exzellenter Doku. Im Journal of Open Source Software (JOSS) werden z.B. Softwareartikel veröffentlicht, bei denen die zugehörigen Repositorien aufgrund des Review-Prozesses vorbildliche READMEs und Wikis haben. Diese können als Vorlage dienen.\nNutzen Sie solche Ressourcen; sie ersparen einem das Rad neu zu erfinden. Allerdings: Adaptieren Sie sie auf Ihre Bedürfnisse – nicht jede Vorlage passt 1:1.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
- "objectID": "Writing/coding-age-ai.html#result",
- "href": "Writing/coding-age-ai.html#result",
- "title": "Field Report: Coding in the Age of AI with Cursor",
- "section": "Result",
- "text": "Result\nIn the fast-evolving field of AI-assisted software development, my experience with Cursor’s specification-driven workflow has yielded tangible outcomes:\n\nCode Quality: By enforcing strict compliance checks and structured task creation, redundant code generation was reduced. The LLM still gets excited to “help”, but now it’s actually contributing meaningful changes instead of reinventing the wheel.\nDevelopment Efficiency: The PRD-first approach has cut try&error time significantly. Before, getting the things i wanted was hit&miss. Now i “read the agents mind” and correct it during specification-creation.\nProject Integrity: Through rigorous spec adherence and systematic task breakdown, the codebase is more consistent, much better documented and decently tested5.\nReduced Cognitive Load: The Cursor rule system has made it easier to focus on actual development tasks instead of wrestling with the AI’s sometimes questionable assumptions - or just installing a third different testing framework for exactly this module.\n\n5 You know.. noone likes writing tests - and the person writing the code should never write the tests anyway. If you haven’t thought of something while coding, chanches are, that you miss that edge-case during testing as well.In this gitlab_overviewer case study, i tried to show at an easy example, that this method works and can yields great outcomes. Even small discrepancies in the codebase tend to pop up during spec-reviews (which can be automated!). Next up would be running those in some kind of CI-job and integrating tools like issue-tracking into the agent instead of simple markdown-files in the repository as makeshift issue-tracker. But not by me for the forseeable future, so if you are looking for a project, feel free!\nAll in all this isn’t a silver bullet for all AI-assisted development problems, but it’s made my coding experience with Cursor much more productive and predictable. It turns out treating an AI as a slightly overeager junior developer who needs clear instructions works better than hoping it’ll just “do the right thing”.",
+ "objectID": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
+ "href": "Writing/documentation.html#teil-automatisierte-dokumentationswerkzeuge",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "(Teil-)automatisierte Dokumentationswerkzeuge",
+ "text": "(Teil-)automatisierte Dokumentationswerkzeuge\nDie Dokumentationslast lässt sich durch den Einsatz geeigneter Werkzeuge erheblich senken. Gerade Forschende, die alleine programmieren, können von (teil-)automatisierter Dokumentation profitieren, um konsistente und aktuelle Unterlagen zu erhalten, ohne alles von Hand schreiben zu müssen. Im Folgenden werden einige Tools und Möglichkeiten vorgestellt – samt Empfehlungen, wann ihr Einsatz sinnvoll oder notwendig ist:\n\nJupyter Notebooks und literate programming\nEin mächtiges Werkzeug – gerade in datengetriebenen Geisteswissenschaften – sind Jupyter Notebooks bzw. R Markdown Notebooks [8]. Diese erlauben es, ausführbaren Code mit erklärendem Text und Visualisierungen in einem Dokument zu vereinen. Für Dokumentationszwecke können Notebooks zweierlei leisten:\n\nals Tutorials/Beispiel-Workflows, die Nutzer interaktiv nachvollziehen können, und\nals Reproduzierbarkeits-Dokumentation für analytische Prozesse.\n\nWenn Ihre Forschungssoftware z. B. eine Bibliothek ist, könnten Sie ein Notebook bereitstellen, das einen typischen Anwendungsfall durchspielt (inklusive Daten-Loading, Aufruf der Funktionen, Darstellung der Ergebnisse).\nNotebooks senken die Hürde, weil Nutzer direkt experimentieren können, und fördern transparente Forschung, da Code, Ergebnisse und Beschreibung zusammenfließen. Sie sind daher sinnvoll, wenn der Hauptanwendungsfall die Durchführung von Analysen oder Datenverarbeitungen ist, die man Schritt für Schritt demonstrieren kann.\n\n\n\n\n\n\nVorsicht bei Python-Notebooks\n\n\n\nNotebooks erfordern eine lauffähige Umgebung – das heißt, Sie müssen darauf achten, dass alle Abhängigkeiten im Notebook deklariert sind oder ggf. nachinstalliert werden und die Daten zugänglich sind.\nEs hat sich gezeigt, dass Notebooks aus Publikationen oft nicht ohne Weiteres laufen, weil Pfade, Datenquellen oder spezielle Umgebungen fehlen.\nDeshalb: Wenn Sie Notebooks als Doku nutzen, stellen Sie sicher, dass sie leicht ausführbar sind (z. B. durch Bereitstellen von Umgebungsdateien wie environment.yml oder Dockerfiles, kleinen Beispieldatensätzen und klaren Anweisungen im Notebook). Ggf. kann man zusätzlich auch reine Markdown/HTML-Exporte von evaluierten Notebooks exportieren und dem Repo beilegen, damit zumindest statisch die Inhalte einsehbar sind.\n\n\n\nWann sollten Sie Notebooks nutzen?\nNotebooks sind quasi Goldstandard, um wissenschaftliche Analysen nachvollziehbar zu machen. In Projekten, wo es um Data Science Workflows oder interaktive Exploration geht, sollten Notebooks stark erwogen werden, während für ein reines Tool/Script eine gut geschriebene README mit Beispielausgabe ausreichend sein kann.\n\n\n\nSphinx/MkDocs/Doxygen (statische Dokumentationswebseiten)\n\n\n\n\n\n\n\nPrinzip\n\n\n\nAb einer Codebasis > einige tausend Zeilen oder >5 nontriviale Module sollte eine generierte Dokumentation bereitstehen.\n\nFür umfangreichere Projekte oder solche mit eigener Website kann es sinnvoll sein, eine Dokumentationswebsite zu generieren. Tools wie Sphinx (zusammen mit ReadTheDocs für Hosting) oder MkDocs erlauben es, aus Markdown/reStructuredText-Dateien einen ansprechend formatierten HTML-Dokumentationssatz zu bauen. Der Vorteil ist, dass man eine durchsuchbare, verlinkte Doku bekommt, oft mit schönem Layout und zusätzlicher Navigation. Mit Continuous Integration lassen sich diese Seiten bei jedem Git-Push automatisch aktualisieren.\nFür die Nachhaltigkeit ist wichtig, dass diese Webseiten statisch sind[3] – d.h. sie funktionieren ohne Server-Backends und bleiben auch offline nutzbar.\nSolche Tools sind sinnvoll, wenn die Dokumentation sehr groß oder öffentlich weit verbreitet ist – z. B. wenn Ihre Software von vielen genutzt wird und Sie ein professionelles Auftreten wünschen, oder wenn Sie die Doku als PDF veröffentlichen möchten. In kleinen DH-Projekten ist es oft nicht nötig, extra eine Webseite zu hosten; dennoch kann Sphinx auch lokal HTML/PDF erzeugen, was man dem Repo beilegen kann.\n\nWann sollten Sie eine statische Website generieren?\nVerpflichtend ist so ein Tool selten, höchstens wenn Förderprogramme oder Journals ein dokumentationsseitiges HTML-Manual verlangen. Wenn Sie jedoch planen, Ihre Software z. B. über Jahre zu pflegen und ggf. einem Journal wie JOSS vorzustellen, dann erwartet die Community meist, dass zumindest eine Sphinx/Doxygen-Doku für die API (s.u.) existiert.\n\n\n\nDocstrings und API-Dokumentationsgeneratoren\n\n\n\n\n\n\n\nPrinzip\n\n\n\nBenutzt jemand die Software nur, braucht es keine API-Dokumentationen; wird die Software aber woanders eingebunden, ist dieses notwendig. Generation dieser Dokumentation ist daher der beste Weg.\n\n5 kurz für: “Documentation String”Nutzen Sie die Möglichkeit, Dokumentation direkt im Quellcode unterzubringen, z. B. in Form von Docstrings5 (mehrzeilige Strings in Funktionen/Klassen bei Python, Roxygen-Kommentare in R, Javadoc-Kommentare in Java, etc.).\nDiese dienen doppelt: Zum einen erleichtern sie es Ihnen und Kollegen, den Code beim Lesen zu verstehen, zum anderen können sie von Tools ausgelesen und zu hübschen API-Dokumentationen verarbeitet werden. Idealerweise dokumentieren Sie jede wichtige oder von außen sichtbare Funktion, Klasse oder Modul mit einem kurzen Docstring, der Zweck, Parameter, Rückgaben und ggf. Beispiele enthält. Für kleine Scripte genügen ggf. Modul- oder Abschnittskommentare.\nWichtig ist Konsistenz im Stil – halten Sie sich an Konventionen Ihres Ökosystems (z. B. Google Style Guide für Python Docstrings oder entsprechende Formatvorgaben für andere Sprachen)[9]. Verlinken sie diese Styleguides in der README. Sogenannte Linting-Tools, wie etwa pylint, können die Verwendung erzwingen.\nMit Tools, wie Sphinx, Javadoc, Doxygen, MkDocs,pdoc und vielen weiteren, können aus Docstrings automatisiert Webseiten oder PDF-Handbücher generiert werden. Sie lesen z. B. die Python-Docstrings und erzeuge daraus strukturiert eine Dokumentation; Häufig kann über Erweiterungen auch dritte Dokumentation direkt eingebunden und verlinkt werden.\n\n\nVersionskontrolle und kontinuierliche Dokumentationspflege\n\n\n\n\n\n\n\nPrinzip\n\n\n\nDie beste Dokumentation ist die, die sich selbst aktualisiert.\n\nEine Form der Teil-Automatisierung ist es, die Dokumentation an den Entwicklungs-Workflow zu koppeln. So sollte die Dokumentation im selben Versionskontrollsystem (Git) liegen wie der Code, damit Änderungen synchron nachverfolgt werden. Es empfiehlt sich, bei jedem größeren Code-Update zu prüfen, ob die Doku noch stimmt (das kann man sich z. B. als Punkt in Pull-Request-Reviews notieren oder per Issue-Template abfragen). Für Projekte mit Continuous Integration (CI) kann man sogar automatisierte Checks einrichten, die z. B. prüfen, ob die Doku gebaut werden kann oder ob Docstrings fehlen. Einige CI-Skripte generieren bei jedem Commit eine frische Doku (z. B. mittels Sphinx) und veröffentlichen sie – so ist garantiert, dass die aktuelle Codeversion immer eine aktuelle Doku hat.\nSchlussendlich muss aber das Level an Automation für jedes Projekt individuell abgewogen werden.",
"crumbs": [
"Home",
"Serious",
"Writing",
- "Field Report: Coding in the Age of AI with Cursor"
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ ]
+ },
+ {
+ "objectID": "Writing/documentation.html#checklisten-und-vorlagen",
+ "href": "Writing/documentation.html#checklisten-und-vorlagen",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Checklisten und Vorlagen",
+ "text": "Checklisten und Vorlagen\nUm zu entscheiden, was dokumentiert wird (und was nicht), helfen etablierte Best Practices sowie Vorlagen aus der Community. Im Folgenden sind einige bewährte Richtlinien zusammengefasst.\n\nCheckliste für die Mindest-Dokumentation\nDie folgenden Punkte fassen zusammen, was eine gute Dokumentation mindestens enthalten sollte. Sie können auch als Qualitäts-Checkliste dienen, um Ihre Dokumentation zu überprüfen:\n\nZielklärung: Ist der Zweck der Software klar benannt und der wissenschaftliche Need begründet? (Falls nein, ergänzen: Warum existiert dieses Tool?)\nInstallation & Voraussetzungen: Sind alle Schritte, um die Software lauffähig zu machen, dokumentiert (inkl. Dependencies, evtl. mit Installationsbefehlen)? Ist ersichtlich, welche Umgebung nötig ist (OS, Hardware)?\nGrundlegende Nutzung: Gibt es eine Anleitung oder Beispiele, wie man die Software verwendet (Eingabe -> Ausgaben)? Ist mindestens ein typischer Workflow beschrieben, idealerweise mit Beispielinput und -output?\nOptionen & Schnittstellen: Falls relevant – sind alle wichtigen Funktionen, Befehlsoptionen oder API-Methoden dokumentiert? (Nicht unbedingt jede intern, aber alles, was ein Nutzer aufrufen könnte). Für APIs: Sind Parameter und Rückgaben erläutert?\nValidierung & Einschränkungen: Werden Annahmen und Grenzen der Software genannt? Weiß ein*e Nutzer*in, welche Fälle nicht abgedeckt sind oder worauf zu achten ist (z. B. Datenqualität, maximale Größen)? Transparenz hier verhindert Frustration.\nHintergrund & Referenzen: Sind die wichtigsten konzeptionellen Hintergründe oder Referenzen angegeben? (Z. B. theoretische Grundlagen, Algorithmen, Literaturverweise). Das muss kein Essay sein, aber ein paar Sätze + Referenzen schaffen Vertrauen in die wissenschaftliche Fundierung.\nKontakt & Weiterführung: Ist angegeben, wie man Hilfe bekommt oder Fehler melden kann (Issue-Tracker, E-Mail)? Gibt es Hinweise für Beiträge (falls erwünscht) oder zumindest die Information, wer die Autor*innen sind?\nRechtliches & Zitation: Liegt die Lizenz bei und wird sie genannt? Sind Infos zum Zitieren der Software vorhanden (z. B. “Bitte zitieren Sie DOI XYZ”)? Das stellt sicher, dass die Software nachnutzbar und akademisch kreditiert wird.\nAktualität & Version: Entspricht die Dokumentation der aktuellen Softwareversion? (Check: Versionsnummern, Datumsangaben). Veraltete Doku kann schlimmer sein als keine – planen Sie also ein, die Doku mit jedem Release kurz zu überprüfen.\nKonsistenz & Stil: Wird ein einheitlicher Ton und Stil durchgehalten? (z. B. durchgehende Verwendung gleicher Begriffe für Konzepte, Sprache entweder Deutsch oder Englisch einheitlich je nach Zielgruppe). Kleinliche Fehler (Tippfehler, kaputte Links) sind auszumerzen, da sie Nutzer abschrecken.\n\nDiese Checkliste kann vor einem “Release” der Software durchgegangen werden, ähnlich einem Review-Prozess (vgl. JOSS Review-Kriterien, die viele dieser Punkte abdecken). Sie hilft zu entscheiden, was noch dokumentiert werden muss und was eventuell weggelassen werden kann. Alles, was für die obigen Punkte nicht relevant ist, kann man tendenziell aus der Hauptdokumentation herauslassen.\n\n\nImplementierung aller Vorschläge als ready-to-use Repository\n\n\n\n\n\n\nTODO\n\n\n\n\nHier noch auf unsere Template-Repos verweisen.\nTemplate-Repos selbst ggf. automatisch auf Zenodo mit kleinem Erklärungstext veröffentlichen?",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Writing",
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ ]
+ },
+ {
+ "objectID": "Writing/documentation.html#fazit",
+ "href": "Writing/documentation.html#fazit",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Fazit",
+ "text": "Fazit\nDie hier präsentierten Anforderungen und Empfehlungen bieten einen Leitfaden für die Dokumentation von Forschungssoftware in den Digital Humanities. Sie sind darauf ausgerichtet, mit überschaubarem Aufwand maximale Nachvollziehbarkeit, Langlebigkeit und Wiederverwendbarkeit zu erreichen.\nIndem zentrale Inhalte (Ziele, Inputs/Outputs, Hintergrund, etc.) klar dokumentiert, ein nutzerfreundliches Format (README im Repo) gewählt, der Umfang fokussiert gehalten und hilfreiche Tools eingesetzt werden, kann die Dokumentation zur Stärke eines Projekts werden statt einem lästigen Anhängsel.\nSo schließt sich der Kreis zwischen guter Softwareentwicklung und guter Wissenschaft[10, Leitlinie 12]: Dokumentation ist das Bindeglied, das Code und Erkenntnis transparent verbindet. In der Praxis bedeutet dies zwar zusätzliche Arbeitsschritte, doch wie die Erfahrung zeigt, zahlen sich diese in Form von Zeiteinsparung bei Nutzern, höherer Zitierbarkeit und größerer Wirkung der Software aus. Mit diesem Anforderungskatalog sind Forschende gut gerüstet, um ihre Softwareprojekte dokumentationstechnisch auf ein solides Fundament zu stellen – trotz knapper Zeit und ohne Informatikabschluss. Denn am Ende gilt: Gut dokumentierte Forschungscode ist nachhaltige Forschung.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Writing",
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ ]
+ },
+ {
+ "objectID": "Writing/documentation.html#tabellarische-übersicht-der-dokumentations-bestandteile",
+ "href": "Writing/documentation.html#tabellarische-übersicht-der-dokumentations-bestandteile",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Tabellarische Übersicht der Dokumentations-Bestandteile",
+ "text": "Tabellarische Übersicht der Dokumentations-Bestandteile\n\n\n\nEmpfohlene Dokumentationselemente, Inhalte und Umfang. Diese Übersicht kann als Vorlage dienen, welche Komponenten ein Dokumentationspaket enthalten sollte. Je nach Projekt können einige Elemente wegfallen oder kombiniert werden – entscheidend ist, dass die Kerninformationen (siehe oben) nicht fehlen.\n\n\n\n\n\n\n\n\nDokuelement\nInhalt/Purpose\nFormat/Ort\nUmfang\n\n\n\n\nREADME (Hauptdoku)\nZweck der Software; Kurzbeschreibung; Installationsanleitung; einfaches Nutzungsbeispiel; Lizenz- und Kontaktinfo\nMarkdown im Root des Repos (statisch versioniert)\n1–2 Seiten\n\n\nEingabe/Ausgabe-Guide\nBeschreibung der erwarteten Inputs (Datenformat, Parameter) und generierten Outputs (Dateien, Berichte) inkl. Beispielen\nTeil der README oder separate Datei (z.B. USAGE.md)\n1 Seite (mit Beispielen)\n\n\nWissenschaftlicher Hintergrund\nErläuterung der Methode, Theorie, Algorithmen; Verweise auf Literatur\nREADME-Abschnitt “Hintergrund” oder separate Doku (BACKGROUND.md)\n0.5–1 Seite (plus Referenzen)\n\n\nBekannte Limitationen\nAuflistung von Einschränkungen, Annahmen, bekannten Problemen; ggf. Workarounds\nREADME-Abschnitt “Limitations” oder FAQ.md\n0.5 Seite\n\n\nBeispiel-Workflow (Tutorial)\nSchritt-für-Schritt Anleitung mit einem realistischen Anwendungsfall (ggf. mit Code und Screenshot)\nJupyter Notebook (.ipynb) im Repo examples/ Ordner oder Markdown in docs/\n1–3 Seiten / entsprechend Zellen\n\n\nAPI-Referenz\nTechnische Dokumentation von Funktionen/Klassen für Entwickler*innen\nAutomatisch generiert aus Docstrings (z.B. Sphinx in docs/ Ordner, HTML/PDF Ausgabe)\nJe nach Codegröße (ggf. umfangreich)\n\n\nCONTRIBUTING\nAnleitung für Beitragswillige: Code Style, Workflow, Tests, Kontakt\nCONTRIBUTING.md im Repo\n0.5–1 Seite\n\n\nLICENSE / CITATION\nRechtliche Infos (Lizenztext); Zitationsleitfaden (Bevorzugte Zitierweise, DOI)\nJeweils eigene Datei im Repo (Plain Text/Markdown)\nKurz (Standardtext bzw. Referenz)\n\n\nRelease-Information\nVersionshinweise, Änderungsprotokoll (Changelog)\nCHANGELOG.md oder Releases auf GitHub\nfortlaufend pro Version (Stichpunkte)",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Writing",
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ ]
+ },
+ {
+ "objectID": "Writing/documentation.html#referenz-websitesservices",
+ "href": "Writing/documentation.html#referenz-websitesservices",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Referenz Websites/Services",
+ "text": "Referenz Websites/Services\n\n\nGitHub: Seite mit sehr vielen Open-Source-Projekten, die git verwenden. Gehört zu Microsoft\nGitLab: Open-Source-Lösung für selbst gehostete Projektverwaltung (git, issue-tracking, …). Community (kostenfrei; limitierte features) oder Enterprise-Linzenz\nJOSS: The Journal of Open Source Software is a developer friendly, open access journal for research software packages.\nJORS: The Journal of Open Research Software features peer reviewed Software Metapapers describing research software with high reuse potential.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Writing",
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
+ ]
+ },
+ {
+ "objectID": "Writing/documentation.html#referenz-software",
+ "href": "Writing/documentation.html#referenz-software",
+ "title": "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)",
+ "section": "Referenz Software",
+ "text": "Referenz Software\n\n\nargparse: Der Argument-Parser der Python-Standardbibliothek\nDoxygen: Generator um aus C/C++ Docstrings eine Dokumentation zu generieren\ngit: Versionskontrollsystem\ngraphviz: Textuelle darstellung von Graphen; Standard-Unix-Tool; Auf vielen Systemen verfügbar und rendert zu pdf/svg\nJavadoc: Generator um aus Java Docstrings eine Dokumentation zu generieren\nMarkdown: Mittlerweile DER Standard bei plaintext-Dokumenten\nmermaid.js: Sprache für Diagramme; kann automatisiert (z.b. durch pandoc, javascript im HTML, …) in Bilder gewandelt werden\nMkDocs: Sehr einfacher und minimalistischer Generator für statische Websites aus Markdown\npandoc: DER Konverter für Dokumente. Kann sehr viel in Markdown wandeln und hieraus HTML/PDF u.ä. erstellen\npdoc: Generator um aus Python Docstrings eine Dokumentation zu generieren\npylint: Linting-Tool für Python. Formatiert Code und weist auf Probleme (z.b. fehlende Dokumentation) hin.\nRoxygen: Generator um aus R Docstrings eine Dokumentation zu generieren\nrst: Alternative zu Markdown.\nSphinx: Mächtiges Dokumentations-Generierungs-Werkzeug, welches hinter readthedocs.com steht.",
+ "crumbs": [
+ "Home",
+ "Serious",
+ "Writing",
+ "Anforderungskatalog für die Dokumentation von Forschungssoftware (Digital Humanities)"
]
},
{
diff --git a/dist/sitemap.xml b/dist/sitemap.xml
index 47a16ea..7299143 100644
--- a/dist/sitemap.xml
+++ b/dist/sitemap.xml
@@ -2,15 +2,15 @@
https://drezil.de/index.html
- 2025-06-13T08:17:37.525Z
+ 2025-06-29T11:34:03.232Zhttps://drezil.de/Writing/Obsidian-RAG.html2025-05-09T19:08:56.279Z
- https://drezil.de/Writing/documentation.html
- 2025-06-05T17:18:21.768Z
+ https://drezil.de/Writing/coding-age-ai.html
+ 2025-06-29T11:46:03.135Zhttps://drezil.de/Uni/Lernerfolg_an_der_Uni.html
@@ -21,12 +21,12 @@
2025-05-09T18:31:10.026Z
- https://drezil.de/Coding/Haskell/Advantages.html
- 2025-05-09T07:11:06.603Z
+ https://drezil.de/Coding/Haskell/FFPiH.html
+ 2025-06-29T11:35:04.263Z
- https://drezil.de/Coding/Haskell/FFPiH.html
- 2025-05-09T07:07:51.739Z
+ https://drezil.de/Coding/Haskell/Lenses.html
+ 2025-05-09T07:10:26.159Zhttps://drezil.de/Coding/Haskell/Webapp-Example/MyService_Types.hs.html
@@ -62,11 +62,11 @@
https://drezil.de/Coding/Haskell/Webapp-Example/index.html
- 2025-05-09T19:20:12.478Z
+ 2025-06-29T11:30:44.156Z
- https://drezil.de/Coding/Haskell/Lenses.html
- 2025-05-09T07:10:26.159Z
+ https://drezil.de/Coding/Haskell/Advantages.html
+ 2025-05-09T07:11:06.603Zhttps://drezil.de/Health/Issues.html
@@ -85,8 +85,8 @@
2025-06-05T13:06:32.542Z
- https://drezil.de/Writing/coding-age-ai.html
- 2025-06-26T21:04:58.361Z
+ https://drezil.de/Writing/documentation.html
+ 2025-06-05T17:18:21.768Zhttps://drezil.de/Obsidian-Articles/Artikelbewertung durch LLM.html
diff --git a/dist/thumbs/assets_task_01jyq0b0wtewyr9wthgg6e8agt_1750971350_img_0.webp b/dist/thumbs/assets_task_01jyq0b0wtewyr9wthgg6e8agt_1750971350_img_0.webp
deleted file mode 100644
index ae23e45b2c83adbff6dbac1cd87e3a3c7dc1b95a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 37682
zcmZs?b97zd_5~U?HXGY!<1}e(+eYIwcGB2R8k>!6+h}at&Uq*8z4!Ot81KJx_SyTx
zTyw2&&AG=>ln@oAi3b5u6A_YEmH$FS0Q`(R2$lg#1M&$3f?t6uPBbq!Cx^K1OO+ig
zjF}B=-D}yJkKQ@>1k#Pbqop|vK(8j~L;<0@L`(cr8tu@kVKD9~`j_geHc
z_^6AXS<>E_!naCE7X13|R9|0a8{t8pqJESWA$NbVj3yfq=T+p;1^@uN61RblJ6VAM$5*J|X9af`4}iYTi?>fFo%~r$?{F78>jy6a%K}bWWj+G$
z-+T&veBZ7gIuDD+q0;3_2~TmK0PCEmmj*sYZ|q*d4}hz;nU1~J=zY2Stfvj*hv?H4
z1-Y2FhS$z}fwjpi0g2AuH@~aF1D8i1yDTETH~=@`>yye;?@&pTPd}jSiTo1olylK%
z8sH6}1cqVfXvF85~p
zdhwWbq1Qkt-?`*70ldda9!
zml6^o*V~MvSWhqLv9s__y?L&L1o((4M)}>C7H}ldVT_io-e+%QPQ5wXj5mS0B(K#z
zt56-Xxs6xDOoy)FuUl9lo#WT|Q>Zi8^Y^5qz%Lg!p26`sHzrT^So;^i`81&UtveY$
z9!zIvvjtzg+Uv``!g}fuZUNI2#;wALiDkuRYYnWf8`C>D9AQ)@1TO2D7&t7atD|Rd
z0$8nwxY-eSBZ{|r=XmnrUYcDWu=8$w8sc!MDSkq{J_rAgb%eiH%i4Tc+N+
zR7Egi0`@mF-38BH-RwUjQ3~&m{zIm5@7FJ{m5`wXogN^%tK{LYOMZ&tq;UrEq@NKF
zVOo3BH`2#d$G)?Q8>#)tk0-_23x8h@O%odWdShGh*;{Wg?=aA1>S6x^rgfg`W21|%
zQdm#GQRA$NusZ4VXU>m^4FMdl6-^PrPSk6mh`{*hE80H<`<=u@#j%9<8B2m8
zr8AB`{##Pb8==@BSucg?N91Z1rTc=&lF!W%ZN1f(O=CWyOW_9v1nZffi)>MXNhD?2
zs*OC~r3K8E?WcDH5dFP;xdNHRaYe$fmfDInarskvx%Ob1^>bPakI9_Q)`c=SQsS1gP`N%
zZi-P;`@3B@Fl1C0h@mMl`EhS^hn_3fO@Fx-|9OXyq1LymvT_ipR0`4Nr}PVNE2=M7w-A)q1G^n)g-;UAlP@huOdC59SL
zOC$KYb<6kfXGY)?J_OO=izqSYzCa57_xTch3g}uOS~83l@Jrr$GZJx7HeXjlI_dQG
z$6!D3?mYXO03Z2j^eNdcDW-<1Jn$pq{*wRiWs8($Q^vmp(GK_zx&Mpy|9g=FhP+MT
z*E5(R7_lOes?W32;nyD<|CdjUz4GyB~4ijwGdl)WyA&@tDnm|$w$jy?TS(+
zVR`M0{_nj&?ywNZ22V~W{7vI@R)v96AQ5X`ffMwvk^FEo_EvPA2Q#zuD5cEq?(|GA35A|(7UVB!
zVCugP%`v3Woj8T5x);oWb5uCg=1=^nLi~;}Y{y%(lO-ixCz=JWa9E#*=Xhl9gDu!K
zR;z+9A6xJ@s-qztBor%R;1P>TGN%8Kub?0q;$S-F3($I==Wyxn4>}(b`3a1q
z9l*^yug+;3FdLFqfs>T(YBSo|_^d
zh?bgSNwiW)%C|#ym_`TZciN?>j(?-0fY7`cxl^`V&Oic|?Y4mWv2xSef4TONepKsW
z66E_$520d&QzPnn1XYd4il#pty8$$9%52rfgz@IVOt%dRig-c773IE*G025Mzo(EH
zrk2zz4VM~~2@|(3>BbFKcCNwK$X^0`>Rdhw23UTChDr9>|7Al`Laf0m=wo?<>lvqX
zY;*dG<^y$2w&*4I{0}tW_wE5DxPX~4OH8Hi%{ql8WF#Id+N2)X6KEV$G4&|$ud@Ys
z;V3qrW(yO5HJ+;QRznRj1o=rva*|xi!Pw5VhUy8jN?<+BcTwSI3So+!2SkujP5lZC
z8gt#)cP+aV${wi)HV7-z$enbw;0SeHn6d5OKoX_I@7JIT5T#HKgjsywP!VEw>)PBX
z%hwTam=p%_9p!4gg}aN58+>aoU7-3hn1?Hq-uj>HF>ffj#(1Bax^9;$8G&N
zVm=U5ZxgJpd%&m!Felu3@Owz1BYze1HWOn8dONzCT`@
zyeacYlIx(!pXM7vdLB*hOd{95wH#_xn2
znHXm>JXpn+HB5P*^wBSJg3iCEiz)bt$RLLf^++05`TdTKYrAS_Jpk?7$K_eq}8|{nJHbyzrM8*>BwfVko7nYOYV|GjW-fjpb7kJRWaUI83i)Vu@z!29q#|%q
zZgmQ^Zxhoy07zq-%k(x_`m_nH-4d^7V@N8`@D?$Ki(Ah%H8bAI-)yd|>A8bhg{v&s
zn>_N8a6;wwxzx;VSj2{R^4R%EEZM5e9%KLs=dvlV;SuneLD-$-CZ)@i{pMmPMmY~%s7a+^j
z7*>3b&v?v*P-=Ax*n9M7Q^ljFa1Doz-PIT!Epkiof58(on;m`S-Y$uh>>(M1w)&z5
zDHSWQ;v2G@{tF>>?$Kce4gOQ}B|E`)&}>qEiSFeJ2`lDi}jMURg4+Z-Qiv0r8?D`JWuM
zbbX6qCdXa+O!Q1MB)covRp_-T-k3f=bVL7AXNwXgr#*j4I0%GfzH|O
zj>l51oV2ws*kfQxeRDTbYDr>L9~ge*tyU
zl{olEJin;yjiymC5bgb7)6%eyCAL0w`7N%
zzY+gB1oa6vf1aGBi{c@VN`$WJspgiZ$gxJlqpS-qmLBvrw9EM+Me#D~0JfxF(MYE;
zpxXqh>_@p{GRW^+Dp8@{9wJO0RlRT4w(v2wJmO~u@{=|`kOv?Pjcm<0jF6MBQquGu
z-@&K#rMpGfDNry!a;?B~>1zg4S;nSE`x~wi+#g`eaFps_zw)nz9i@k|1)faQIlMoJ
zJlr+9=o~y{&_0gi+@i^1M
zPZVj|G8aIjW7G!p^V~)oUd4KiqSfe<-#lL6iX|>j+Y8zdteTpo)`Zy#Vj&=uY71V+(0`u!H5r`-eRu5WkhI5%f$)dptWf3xy_{yD&go}%=
z4M`ia0eE`$L|Q_UrOehXpUnhkrJfso+G}EGf*DX&{ce0;qO(;Ex%n@a){{b*CERYS
z-3x{7J3T&Hp|z5vfah_FWJ!z+O<;Q$Tot97@I(S3?pESNa;OU1Iaw#;i$B-5;
zvOm%)JM`~X32@m2%pcQiO@%PWpYC$ykq67GlJ7u_iR}D8laQj5^xuFAht6N^Dj1Cj
zvB+Env6hdhuJ9bcJnMTZPV`AZ+n#Sc&BI09KK{3YKSr9zmykE(b7)$E>xeSSedEJg
z4QDXy0W~}1`sgv=Z9Q{m*CAcTke(qNk1UcxW;ZRZ=*%PeKjqpZ(KY?;ONttdCG91S
zl(7(5PC{zHCWxjieCP@W9$P>CX0J!n!2dL5;L{%a3W@aqYQ#(6;cD;En0_L3wPOgU
zj1P!}ikMKHYoWrSqIX-V53Ey(nNpDC`+JOvJQQ?+;Ck%3OcPHQ06Pa|gW_MBX4>!w
zqKEJKMm7KKG4*-qd)^WdG_P^@Za=!izls92N1WxG2bL(6?0t-%+O&5)8~x<39Lz0%
zex{oaMTwOYYFPflqFMJZ59f+2#ww^HCHT{)y0j6;N17`~(1~WUv!ggh#6+P68k4|I
zCub(Pa`7_J=kOaBVE=ZPCgHsgY?$`RC^!C`uSR<7eHP5=+g9k(3`O)}X9um!!z#*M
z(6FC1`B=2!?RDghzQE|0;c3LiA+fQ2?wN2vQx&?!9c4BkxOf6Tx&<*ZL#!(0=r6dG
z$hSsOo_Ux%OAmXkl5AX_MC~}mZTpIEY3>R#s(MMUsR#ofGLLcO$`#Xe3|5xWTT{#<
zSp3jJDzyK+d2C7HOK_65)4aOLC9bDDL{gp9Ma|dRjMFs9=x-lAAvIT{^3MC+(@+>`
zB{?fjCwjZhloHC566VHpSIEdNJ%7p+R-&i=(vPj$t2cvFyyB{n;;q95GLv7#+#pX|
z4}(L2US^z@au;1@+RB!V05jr{dCsSHVBt^r2mPS>X)d4%cKl3roV5KThb*rVMB}x<
z&3CfHu`*~knuiux1=UFYiYgLQ{j{-e_Ei7y*EgV)&@G8M7W|NY-fH1povZyT4k*J+
zgFvv|HKI`C7Du|jCC6n&L)5QtCgRBb1XVFjRfWTc9?R@3qbo(N(etoWcZ$;)<9{FatzwDMD+
zoT$P&KH~PnU(IAC@lQ4ZRSZz~b*XfKibSf;8L__#(>p>BsNFs^=YJOov?o5+Ir(25M*d+`xMxh22<8r)
zLK*|z4uS7T>FlKc4!Ebrg-Dzaxkg9HKuWv@>jFIVo@$kV8%b{?4}ag}JMRnr{%!0(
z@Zn1T=Xn1ME3nxAWeX6g8<)Vpm;R&vYr*3Ff4~P?3JVdP45TFT$lkz#fkoiOJPjf=
z<$p5%!;JX54S~P50dT+n5rQ8f1ll%_N2FA3fet`U2l3>{9>M^F@>g#T%rXZE2K?6z
z`9S*y=qr6_;ivX@AG}|B#A~3O?ggMER_(HfN-bbEmw)N_*9Q0)V>&H%Mz0w-qH;Dd
z(CAqQYUYxhUHlKH1EMbKK~M@;8aT!Omr2-ce~;Y1qFR57OvWX!E(j=q{~&o}Q(Pe0
zg##@bY(8eU;lEn*e>?oA&)=5`n~=i5kX9ihRe>H?_z*9DaOdPseuUn^)i6cNzXJJ@
zFaPrbJ}jK1m|7Q@wi*0-G~JH{;xi4WZaw`xieD-b2D$=mGoPQC
zKX~*fnn8%`%ZEMEmkW@{0p1^eJ%I4x^?W!R_{=&s{|#dV
zLCQB%$PWkTBW534{%4i^Gx<>dc^{_vK;sO^s1Ibl|M^9KBYgGOsQ`xZBWv^(rCE$k
z@HgWm{`!kp`!^~pp21Eu^1$r*u#`ILK41enS%2sGU%~>N#Z#49p#3!f0`mScgsx$B
zc#(|-)sHGsTogUNu=#$0dZ9Iqh0a!x4MTAQu0l=4Wm0WqQ-2Z${Ss6rCEA%aBb>I$
z^EDLY)R;6xK>mi$NA9sA!n;HG%fu)w#!rFJLLBR&aqCV&v2dG#(!3$T<}RV^sij2!I&7_>{wU~*yI{~@imW_&
zZDkFlL!-o3^rp&HD{-5gF-tT8-rhmYHK?TOB}ArsoLnQGx_V)2rWNZm53~X+?`Li-
zM;c|cFgmFI?o}6V1B$$;@9bF_v
zHKm<9k|;g*Q=Sq}J|9)MYim;G<_ThP`7rS3p|b{HVPjKJiX3gNiTI1wpmJr+g|FT1
zX|h^PE|Z>3?93p~J=|09ktJ-SOB%``ln8LK61ZRV)`W1$fPuHFNN*;9T%jf;f&Jil
ziKvI=JGeo{G170g?w(qcajofG!@X|?%P|~dKK@C8Tcx>-e=K>EVdh0%1228uck(W^
z+W4FDB}shC>eRE%2UU-J3=gXagwjTwzh^)XU!neX-ay+{E!FckJ6F%5kn@~a+lIuR
z!0HIPFj@1lN$Yzon*L8`bwQYgv{Ew{0`WrA7!Sy*x6Cvn)x$~@Ms1XVxO4X0Ckxc2
z)YVF{VR%C$;`H=i5TUTi&D;Cq1T0eG!pAxyVAl;T8im#DuKV~g67|7uu3tmj^Vn%B
z>;p5i8ov-V_KGvczq%TZcxmZ#e3chr>)g8oRN?X!Glf
zk+m8fe<-81-fdL-4XERLhCHv*OXC+*{Q(e#%=Qgd-RDf+Dn^vCYf1}NzsZZ4R!wp`
z9B+q(RJ=shY}EUG{_aI(47W~P4)=v#aME{y=7klJwH{XhJ4Snv=Cti=O+lBbZtNdT
zFblhWGQzj13D4n-!BC%Tj=V>>Oo4{w4$O&hkl(?uYJB28#pq4=eeJko=}Ei!NZS*x
zY&W9S2GzOnSa%@Lx$iBc$F*2qcdSAM78}!OFneYj+pvaZlS}w=OWiaf|4v6+-?^!eF(b@y1N9uA-DvfUA*t!C
z-^#xLg_hP>_Cnfg8d`@T%#AWlak}eOmsd0y>8@AbLbcdBR
zTKk%inX#&Ns%qd36X22c>vAVlL}`G?@#J7THa)eoi$Mm=&|X;YRxBmw8+;TV$VDM8*pJ(53ZyZR|VNnn4B
zGv{T5_a`g{Q!n1dY^XrNA(E1;jOPouQT?cOllEeZ#a;3UeIq8*Ga%#G^9qy(j%76J
zUL-#BTm5N(*J&yJNeFCaHXk<&D-t96TV;*@Lj3W&L{ftWC7bz!K!!{PT38cLkiZ+X
z50gCSels&gm3yqX8mcCiBr0H1_$`qKHNJ0FJ8j2RX^NFus3xa|QKc_IRxB#R@<3F^
zYLWu;$F-LC1X9{!)Q*saM%K!EbRcVw&rcFjK`9K(qAV85
zA=NA>$zNw2o&W$Iq^v8qC5()q<#nFO(0sn;_-Ua2et6AG&|YX;o$_Ls?RliUVBt
zcQujuKH=$X6hMGvJ7^4|#faeyyBk$hQp+8O+(9|0`bc;pbOd-qc%b8`5Bbrv4r<^ad+8Mr
zizeFf@~Sxd{l+pXYsSQ7TJg9=!llzD|Mk0!XC~K0<)*SfbWeuf+O7Yu{%lc&|OE#|BR~>R23;d>^Rfd0&M{lTq>>
z-&@q+xtjzYrE_#cb91#%4v7272)B{Vev|~kyr#bR8ukrN<-arSt{zaUx35z}?OW~H
zsp8y-t!ox}i$KY?O6EsB?wg2V*`TZa>UWfD@fIK!y~cMDTF<=YC5(dSF&+-t!;9OrQd5)`px~vIj1)ex@avfNy
z!ss%VXcL6`yJWmFoG@v--}LXje*tNtY0__I@(Zw1V5RUC4cv}{f@p@Z-^&PI%PO)G
zqDtq0BKI&&C?gKDOEci60uiO4{*wC+@x|X)9)9+2CFf*^G^b_QTIHDNf&>&kr7U~4
z#>qfYTG^fyn=#k;?Z9@m(2Q6ES84N_>IT5_*;doFE{+HHosXw^#gF=ynGSJ2rjJnS
z`n3!(aU{z=BfCOOXu+(G>xY^W+JZk5wN474*(za=OR@)QY+4)47{ky2RD6siq_06v
zQq%s0#M#LkPLQCJ?WPJ%m~wC>n=N3=5BE=aVQ3ayj^29W7>DeBD0-n49xmp_@vE1A
zOSu;7kJe_Q_LUg0`T=oi>k;QMj3nwTQ;H@3jHJw#&U_{L7PQ(tr&Yn|?T@us8<7G+&twxJ*J?Qc_
zDHJCj;Ru25Hs)aFaLc1x@fcx&}ZA#8?g_FO0V>r-c{93B#AP~>g5jV
z(hi#C*C()vfuDI1=X?yO;oEy(w6^B_l@qr2vZ3YtDsx;sJniqSn8a7I?NohN0Qa(O
z`)^|z76zHgzvCPqL*FH#;gvz>CX_m`45%ng9`l)AU<6gAw;Y)28<)?v{oHJw#YU=L
zSYQ?(Fyrt5>xJB}1D`mbrj0dZjv>bx@;8bS(Oo}d7%m}ufm3DX@m_OmQ7Mq;NyZE1x}p{dT7p$e
zt{CQsB7BEX^wbZy#-PXae5d_&5O=32Zr3>Shq~{~pxy&|7s(KADTSr{`L=qv&g#{b
zW^x|#K5_lius!7&T8wUM4|v#T-KsKuJdZ;i4(8vjB-`8asB2a{mE`u>=H5qeg;y&o=MC?c45u24%<*JxE(0ju1w
z^ECP79W*1z(19+w(B$%#vL3OmFEx!YZ!50&nF)XIb4XQp{kOP?WV|!O|#nHo#V5jHRl2)jDj4g>UIQA#|==fPO+VriQC@R+A
zi&xww{qamE8DAYGPmR7AHg>ALwR{aqiBvVr2*hu>I2Sz!g<_m9-{UVUO2%g_7P?$!
z&~BzrGS*`Usid$;w77T1F13$RJknOnEz*cYVA0noAg#8%tw5F<&5H%$c;TVxUafVu
zpzdXx^D16Y1m?7WuB5iVNHk5ps3>SlsR5_gmaa3;H3J-v`Pev+!y
zX3~|!&!|=Ef>_&tHAA3L;{gN@=|FURuZm)OK+0F*27Q;mpUXvBM`{ak5cbQ`boPe%
zZN%`&c^RwNj*XA5eji6!3epjg?ji8WXJG+!bim8dBoFXalVkt%kia`Pw6$hhCjKs4
zj1;AP^uh$WSz8|d&HT68XNxWDjala%pj
zMC^_q+kaaNOqo*Hl{zI=qt!=mi_@2hjPMi_zI)cflOn5H>l3>5ov>$u*E?GX2KM|S1$O2K
zDB8U+k?!Dro5*(YlvsDBdi>(Zrtg~Sy;+OCtO{_&?ULhiA>p6?@RYV*fv!0QEZ&4x
zC9`ZhaB%vI^1r@N&C=^N<=IuKIteb?yC;X_TSsNtc!jIPd{Oq6%zI9iWd&=DU#@krVC-3wDZMbYSfK`;xl
zE!CnzAT4kc?Uk^Yq$jiJB<^wwJgVT%$dEQ?Pm(_|DlT9lp01X$l!jq9hkzMM8VBw9
zha35QL(6GSGtBX5B`cySK4*TQHh0e?v068@34;x)(cwPlIkzjm7R!#{d+ORkb9`VU@;Nv96czX4HKQQ%FDYI_ko2*U?9?
z3G4hAu1J+tn*}`zEi*%QY_!+Iu(CoX1wr~*X_=*v560f;3@wq
zFkbZH>bK&n{(d1)drPKhBKJVz`_}2XPG()E|42Rx+r1|rjzEA8N??lY&M
z(-fAwbm$z|NaNRz!!l|G*t67UEha2dF4ZzAp|c9hdvsSX1dL&P6-r=$p!?fw6G?AdYl+TV*E>aW#GB8h@>PJz$~@2rs@nKCWM?N<7MeWV(h^5_1hu
zxFa%;TvM-b66ASJPeD6}{=qA19ka3mZaXCbC}k$oLNh?MZ)J)lRYJZmY|J&l96=w;
z@DYbW4vCDGI?KzXPdYp9<7_9A47@KZVF$Gt(7`f4(kt>%c%~JB?RetV<*$2=$?0eK
zQgdM&*paDZL(Ga#sY#60orU>=sU;IO$MW4)Lb+b2S)7Rmgi|Oo0R){u2g?7>kPP7p
z&ah|cKw?|7%LWa2COOc}xUL)O^O%{EEvv-cCe2wal5nvhn=3pg945l$8g7uE>nVDV
z&v(%Y9SlF)(6i{Ua?Y-!ts_
zG9SA?=ddS#^9{kxKmj?7N_cQU?-^9c1jU#d^-)-UUeae`5jeF-98L3;(ru}d{2s}%
zF@(}W!W5x&U>?ozYAgN-Y*8Tb`P(}yqR
zDLve`H-~8!FrA7FXbbxqrA^r+Q;Fl(DD4niE`!IM<2GlHhr7w`=et-4hh?$8ql1i|
ztcVjQ^atVF^})r6{BL-@Z^|#){Pqnw4&J=C#;`xv-GVNOqsw%mb84-mPWSaj>hi%S
zMc;e%@fHC)RL$$oK0c38d!2;Nq&H0wyq7uO1OtXUk=#yuY`UR|dP9@mlVEMK1h`H|
z4c_ATE4{r`!N9o59<}x`?<^*JqluZ4yEXnGgNr%G-`hEJjlObb-mL{xM8nr7vw7kt
zXG+`|tSF@}|3JwZ0thtCms62upQ_YN24M%_%IiL_eO+oD)O?LlM)NYhg~dVEHX;)u}>H(VK_|F?ijaAa46=p?Qc?dK<121MrSHVf^jR+gs
zr=>!tU*O-&vRVNK{?dT>=6<)4qE`VLbPl&fSVWqzd|sZR#`d&XG7je^Zto{36)=PK
zqS6&ua@hqd>Ov@Pq1x>OzLxCVgN4nfY-+FThGN`Es-~mqMq$E?FPr%OmMWea2quc@
z1rZ|^1Fk!K&RYO}snIGvhw9W-X0IK-2CckS6h>?%GIKmtB&JXcukjSFuA$4{7B|;V
zn!kKVN;#Jr_0@w<9AlQ6S=zOZ^o7$4d%UA0mIk2b+{6qllbc7=vnh#z+!{Sc*hPf|-
z97)FJVG#4BL2tH8S(W2T%_^-V2FSNdyLg_jmX>|~+eGfc4gTOwOp*Z>?YOM1XF?7}
zbL&TLM$4aAePJuNh?m`@vfo-znJSj(Is@1FlhM~-j-M;
zW^JnNIq`^T$g8F}DqVOc0Ykd(vg6>{z5bWhY@u(TEsTp6mA0Qg)doRwWu2uz+r_d-
z^rkjQMG6|Q&h>uo{bI)w!{jd7ciqd*D#KP4Pg7s&9A0!rDR9WQGF)20IZ)4iWl56A
zTe6F=M&@}Z6(1PP_uFb}T&)+>nbS0|nKh`>6gO&*L@kCHiKrd|c?<&Q0DztzG3-Un
zag}mok(k*DN!Q3R+otW8l9?e2-xmuV$09Uze6KrgljK1mt540V|B!8X?iAu&edS8x
z55b8>_=WRLc8LnG&A!WK1+wGF=i<>t?P;Z@f0^uZmxCp4pAC5hwFmmnLd%VkEm`0`
z0mkxOu}K^j2O3jCIYKBVKzSfSEU&~tP~^I~iUa-=`zcmKfVWfgw~$tWKok13Z#eOe
z;M{0G>baYV2PP*kmDlmI>2W}(TQ3XdZZdtI5j#uUw@wx(^&Zz0h-Ku84~y!(%Qtzw
zIS63IU|#Ycl$*%Sxm%VhsqU^}c$ir)Fg15yKXn^vQP=+r5Qe|N0#`T~f(WohH57r;
z@_p;Yug*o@mlkip(f$FTn@}bWSfzcHvxv3WV@ip~UW0*^ZJ^~OZArps_E$E;gBW-S
zuA^VPt31%~@!aj{h2SEV;INacASN1LkfgqjFx^bhOw`)Xu;-YcwEB5-Z#A=VkUOUQ
zfSxA~4Oyla#eWD6Ks9qDK2Am#utO#x#@tLy`X1Nj(d%4DohWV)P!XPax!qW4_RFZb
zo$nmgpE1KbI>c$mWn`yv2&kJVHZId{HqPCN}eYP~i9Evs1l^lFv7Xox_P8X|?BmSkTX1+7bJfOV|Wn5M_ZHCOJ%r
zB;RC=!@5%DFy|Fa-U9D6=}|2oE0V2#Hh+icP#3`TYRNq7^wqa!eMom2jRYC#yTlxt
zU>D@LwTBr7QuB(7pcbzGtnqgZ?jv8v7ignTw6r!yPaIu{QHd1?y|27yZucU+k`hb0
zTLeej#KPXjZ6*NcHDez5LJnfQWXm@5snL7QS+i!zm^anJ$NFmL^tmH|$nd~|up@`k
zw1g7P8JYWA$=l&h=uz+6ZiLqO)`dg$&pRDP$V(Dp>UV{)ua;cAjjQlWwVDyT&$3QaFGSU8RcF3Rt~J^)r=s`jl<(~`<<9540-0*Wiw>8
z`nEWFMYnheEY`Z3>LkU>pWXy9_bOvCvXXMqRfrzEd^1Or$WET17YaYyN_t+lv=t3o
zTr^=wDme+aR
z67v{)PTtWY&sA3@kJ*n~2L@sp{qR9k4H|wrn4)E-IP6Fw@yOPo
zFGNXrihP44Qln365!ZnH&EX5hY1z;9+GEBayA@4H5sjL&RO1PAbH{M+9*HpR3B@npI~y
z>c#2^qE#;WW#TMHrRv6RVI6LyNu`eA>ew*r9K(T
zh9q53FG%Nk;4T+4bJ)1|2mPbJy7?GQDMu}}zF#=jaZrVm7^x8uZ$m#)6514PURM2d
z{iAyZ3CCld!vy}CsQsHy`!Z8_pj8ycuG%5I+ogykuH=?sm1$Om(3TZs7FFxs?9BNq
z_RQS3g6Y8n&*8v7A{6*HodKe=v6!ZuZ>aLWPpBLtw2~?%b{bk_YbF#TWG{nMkaTw5!BzMQq@ed$_<%55a6CJuhFU5YNX#&pXm
zm^|1di~4d-ofQZkrIPwpALp%TJ-{70KUxeYLSWt?$`eVb{jpxS-?!Iu-Z~#ka)i)j
z>C3zK@qW5PNqP;vRHOLgF^WN3r~k6taYGygsEIi(SA2c0PjLK+bZKF9?7%V>=U9m%
zGJF_THNB+H8K*R*C{Y=A56k`Ddx%RVg462+n(o!e-hxg*R*LGOps2
z)vZ;T2t;Zl63DVl()W9pbf6E}%nxpdv9-il9wvW~uc#=W_TU>io)6W6Nsrdux-RY%
zh>E{^qQWDAhX~sKOhYI6L%|
zMg?>xDPUIS98+p8)^TLp(rTegyveckRl*ISyU$<_
z`%C=UW)Hta*mUag8-XZ}{`p2J1`07E^)l^jQF|l`7L_zpnwx!8_)^@NPCnXH$}U4^
zmuSQio6A#xe+)xx)&Gl&{FNrNvkuLqRRW=5BdJ33&u3>)i^4`VYBJsm$cmReD-ZIU
zF4TDRiG8cZxKBMdr(iH&!B4I{&cLGg1CZ!$S|DZHK(O?!7zw8GW&Ly{N}7qs^I2^H
zw#rBsWtfnp9cZayi#oZs3>69*WYIkdEs+%2xfnFQJIdjY^~C~ti)=5Tns?l>SrRoWwE^pu_iKjKbPI3VEo?N3X-DjLbC=f0bS%HERvT4_$_80t8VnEQl0A1-go?ExIw1+}y@On@>KtK4yw#bY{$37Gv$AO-_2Q7)&;A|uWcZ5L
z<2@YOT7eI9qBA(8UEYYvN6?|wHko!J0_sNK*)bWhOomJ{_MW6yu^Gn3eR~@oqousE
z+Ygoo3omHh5VM1>#+mf9uKG6-hq&G%+>jsd