Artifact Introduction

Welcome to the Artifact Tutorial and Simple Quality Leaflet!

This "book" (it is a very small book) also serves as the tutorial for artifact, the design documentation tool made for everybody (including developers!). You can get this book on the project's home design documents (TODO: add link) and it also ships with the Web UI of every artifact (just follow the docs in the header).

Artifact is a simple, linkable and trackable design documentation tool for everybody. It allows anyone to write and link their design documents both to each other and to source code, making it easy to know how complete their project is. Documents are revision controllable, can be edited in the browser and have a full suite of command line tools for searching, displaying, checking, exporting and formatting them.

This book is broken into 3 main sections:

  • Artifact introduction: introducing you to the syntax and basic philosophy of artifact.
  • Simple Quality: an interactive guide for achieving high quality with minimal effort geared more towards beginner to intermediate software developers.
  • Addendum: complete list of features, fullpage specification, etc.

This is the installation guide. For more information see the project home page

Typical Installation

artifact is compiled for linux, mac and windows. You can find releases on the github release page.

For Linux and Mac, simply download and unpack the tarball with tar -zxvf <release_name>.tar.gz. Then put it somewhere in your PATH

You can then update to the newest version with art update

Windows

The recommended method of installation for windows is to use the scoop package manager.

First, install scoop from a powershell terminal:

iex (new-object net.webclient).downloadstring('https://get.scoop.sh')

Then install artifact:

scoop install artifact

Arch Linux

In addition to the installation methods above, Artifact is maintained as a package on the Arch AUR by @rubdos:

https://aur.archlinux.org/packages/artifact/

Building From Source

Simply execute the following:

git clone https://github.com/vitiral/artifact
cd artifact
cargo build --release

Note: you may need cargo-web installed as well.

Do a full suite of tests with:

cargo test

Installing with cargo

Install rust with rustup and type cargo install artifact-app

Note this may never be feature complete and is not the recommended method of installation.

Here is an ultra rough overview of the tool for those already familiar with it or who can get up to speed quickly.

An example repository you can play around with is here: https://github.com/vitiral/artifact-example

Useful Background

Useful commands

  • art help: get help
  • art [subcommand] -h: get help on a subcommand.
  • art init: initialize repo
  • art serve: open an editable Web UI.
  • art ls: list/filter artifacts
  • art check: check for errors
  • art fmt: format artifacts
  • art export html $DEST: export a static webpage

Artifact Types

Artifact tracks "artifacts", which are design documentation objects which have a name, some text and can be linked to other artifacts and to source code.

There are three types of artifacts:

  • REQ: Requirement, why your application exists. Also used for high level architecture/goals. Typically these relate to the user in some way.
  • SPC: Specification of how a requirement will be implemented. How you will build your program. Typically these are to document for developers how or why something is implemented a certain way (from a larger architectural point of view).
  • TST: Test. Details of what to test for a SPC or REQ.

Artifact Format

Artifacts can be speicifed in three formats: markdown (.md), TOML (.toml) or YaML (.yaml).

Markdown Format

Artifact uses markdown (by default) to specify the design documents (artifacts). The complete format looks like:

# REQ-name
partof:
- REQ-other
- REQ-foo
done: This artifact is "defined as done".
###
The description of the artifact goes here.

You can do soft-links to other artifacts:
- [[REQ-something]]: The web-ui will have a link to REQ-something and `art
  check` will make sure it exists.
  • name looks like: # REQ-name
  • The partof metadata field is how you link artifacts of any name.
    • power user feature: partof: REQ-[name, other] is the same as being partof both REQ-name and REQ-other. This can also be used as list items. art fmt will always convert it to the long version.
  • The done metadata is an arbitrary string that adds a 100% completed and tested sub-part (if it has no other sub-parts it will be 100% completed and tested). The artifact cannot be implemented in code if done is set.
  • SPC-name is automatically partof REQ-name (because "name" is the same)
  • TST-name is automatically partof SPC-name (because "name" is the same)
  • SPC-name-foo is automatically partof SPC-name (same prefix)

Note that if no metadata is specified you can simply write:

# REQ-name
The description of the artifact goes here.

TOML Format

Toml used to be the default format

[REQ-name]
partof = [
    'REQ-other',
    'REQ-foo',
]
done = 'This artifact is "defined as done"'
text = """
The description of the artifact goes here.
"""

Settings

After running art init, your settings will be in: .art/settings.toml

Settings:

  • artifact_paths: paths to directories/files containing artifacts (in .toml files)
  • exclude_artifact_paths: paths of directories/files to exclude from artifact_paths.
  • code_paths: paths of source code containing #ART-name references.
  • exclude_code_paths: paths of directories/files to exclude from code_paths

Implementing artifacts and subarts

Writing #SPC-name in any valid utf-8 file (read: source code file) that is in a code_paths path will mark the artifact SPC-name as done.

You can also specify subarts (pieces of an artifact that should be implemented in code) by putting [[.subart]] anywhere in an artifact's text field. These can be linked in code like so: #ART-name.subart.

In addition, artifact supports specifying unit tests using a [[.tst-name]] subart. These subarts contribute to both spc% and tst%.

Example Artifact:

# SPC-name
This has [[.subart]] subart.

Also [[.tst-name]] unit test.

Example Code Implementation:

#!/usr/bin/python
def create_name(raw):
    """Documentation about the create_name function

    Implements #SPC-name

    Also implements #SPC-name.subart
    """
    return process_name(raw);


def test_create_name():
   """#SPC-name.tst-name"""
   assert create_name("foo") == "FOO"

REQ-learn

Welcome to the artifact tutorial! This file is written just like artifact markdown files are. Artifact files can be written in a range of formats, the currently supported ones being markdown, toml and yaml.

An artifact file is simply a set of artifacts, each one written like so:

# REQ-NAME
<regular markdown section here>

Artifacts can be a requirement (REQ), design-specification (SPC) or test (TST)

The artifact you are reading now is a requirement, therefore it begins with "REQ".

REQ-markdown

partof:

  • REQ-learn

Artifact files like this one are written in a slightly extended markdown format. You can read more about markdown here: http://commonmark.org/help/tutorial/

The "extended" part is that artifact treats the following syntax as special:

# ART-name
<optional SPECIAL yaml section here>
###
<regular markdown section here>

Where ART is one of REQ, SPC, TST and <optional SPECIAL yaml here> is a few items like partof and done fields. We will get to those later.

SPC-learn

partof:

  • REQ-markdown

Anything starting with SPC is a design specification.

Requirements (REQ) should be used for:

  • Detailing what you want your application to do.
  • What the architecture of your applicaiton should be.

Specifications (SPC) should be used for:

  • How you intend to write your application (lower level details).

There are also tests (TST) which we will learn about later.

SPC-partof

partof:

  • REQ-learn

Artifact uses the names of artifacts to automatically link them and track progress. This makes it easy for the user to intuitively link together requirements with their specification and reduces boilerplate.

For instance, [[SPC-learn]] is automatically a "partof" REQ-learn because the names after the type are the same ("-learn").

You can also explicitly link artifacts like so:

# SPC-name
partof:
- SPC-other
- <additional partof>
###
<regular markdown section here>

Here is a graph of valid partof relations between artifacts:

  REQ <-- SPC <-- TST

In other words:

  • A REQ can be partof a REQ only
  • A SPC an be partof a REQ or SPC
  • A TST can be partof a REQ, SPC or TST

SPC-valid

There are only a few rules for defining artifacts:

  • Case is ignored for all names.
  • Names cannot overlap, even in different files.
  • All names must start with either REQ, SPC or TST.

TST-definition

TST artifacts (and subartifacts) are used to document test design and are the only way that an artifact can be considered "tested" (besides the done field).

Artifact makes it easy to track the "progress" of your application because art ls (and the web-ui) gives you easy to easy to read completion and tested percentages for all your artifacts based on which ones are implemented in source code (more on that later).

SPC-implementing

Artifacts are implemented by putting links anywhere your source code, i.e. #SPC-name. There are also subartifacts, i.e. #SPC-name.sub.

Subartifacts are defined by putting [[.subart]] anywhere in the text. These artifacts are used to break down how to implement an artifact in pieces which should then be linked in code.

Unit tests can be specified by using [[.tst-name]]. These kind of subarts contribute to an artifact's tst%.

Simple Quality

A short guide to quality best practices for developers.

By Garrett Berg vitiral@gmail.com

Introduction

This is a short and open-source leaflet aimed at helping software developers improve their software quality. It is also the primary user guide for the design documentation tool artifact. Its targeted audience are those who:

  • Know at least one programming language.
  • Know revision control. If you don't know one, learn git.
  • Want a brief guide on how to have fewer bugs, re-designs and headaches in all of their projects.

This book is shiped as part of the artifact Web UI and can also be read at (TODO: add link).

If you have suggestions or edits, please open a ticket.

The goal of this book is to make developing software simpler and more fun. Think back to the time you first learned revision control. Think about how you were backing up files before then? Maybe you were copying folders to some backups folder? Maybe you were not backing up at all?

However you did (or didn't) track your changes, think about your life before and after revision control. Things were a lot different, and all you had to learn in order to use revision control were:

  • Some simple vocabulary.
  • An easy to use tool.
  • A new way of looking at things.

This is the central premise of this book: you don't need to understand technical jargon or complex test methodologies to realize huge gains in the quality of your software. Some simple vocabulary, new tools and new ways of looking at things are all you need.

All code/documentation examples in this book are public domain. For more information see the License

Why This Book Exists

There is a lack of good documentation unifying and extending quality best practices. While there have been several advancements made in how to develop quality software, they have largely been piecemeal and lacked the tools to fit them all together. Several of the recent advancements include:

  • better revision control tools and best practices
  • new emphasis on unit tests as part of the development process
  • linters and auto-formatters to help projects have easy to read code
  • more emphasis on documentation, including inline developer documentation
  • the agile process and associated tools

One of the things in common about all of these: developers tend to agree that their lives are better after using them. Programming is easier, more fun and more fulfilling. However, all of these lack the processes, tools and vocabulary necessary to bring them together into an organized whole.

This book will give you the knowledge of how to unify and track all these quality best practices as well as give you an intro to artifact, the open-source documentation tool artifact. The tools presented in this book will not only allow you to write better software, but will help your day-to-day workflow in the same ways that good unit tests do.

Starting Your Project

The primary teaching method that this book will employ is "learning by doing". This is an approach that many developers are familiar with and is used in some of the most effective tutorials on software development.

The project we will be implementing is the flash card challenge created by Open Hatch. There are several reasons this project was chosen:

  • It has a clear goal with targeted users.
  • It is simple to define and yet can be extremely broad.
  • The guide is written in python which largely reads like pseudo code. You should be able to follow along in any language.

One of the best tutorials on C (in my opinion), learn C the hard way has this to say about itself:

[This tutorial] teaches real robust C coding and defensive programming tactics on real hardware rather than abstract machines and pedantic theory. The book emphasizes breaking your code on purpose, and in the process teaches a plethora of important topics.

There are three important aspects to the "Learn The Hard Way" method that this tutorial will use:

  1. It is designed for absolute beginners: you should know the basics of a programming language and revision control, but that's all you need.
  2. It focuses on teaching simple concepts and tools which deliver immediate real-world value.
  3. It uses exercises as the primary teaching method. You must actually do the excersies if you want to understand how the tools and processes you are reading about are useful.

Before we start

Before we start, install the following tools:

Now run the following:

mkdir ~/learn-art # or whatever directory you want
cd ~/learn-art
git init
art init

This will set up your project as an artifact tutorial project and initialize git. It is your job to know how to use git as we progress through the tutorial. I recommend committing the files you have now, including the .art/ directory that was created.

Exercise 1:

Create a README.md file in this directory and take notes while you read the flash card challenge webpage. Pay close attention to:

  • what is the use case (how will this software be used)?
  • what are the inputs/outputs of the program?

Then write a paragraph answering the question "how would I develop this application, knowing only what I know now?"

Specifying Your Purpose

One of the most critical pieces of documentation is your purpose documentation. Without purpose documentation, it is easy to get lost and forget what your project was trying to accomplish.

We are going to start by writing our requirements in our README.md. Later we are going to use artifact to track them.

Open up your README.md in your favorite plain-text editor and write out something like the following:

# Purpose:
Write a flash card quizzer from scratch and learn about
quality best practices while doing so.

The example tutorial can be found here:
    http://wiki.openhatch.org/Flash_card_challenge

It should be easy for users to input questions to the
quizzer in a simple and open format. Additionally, the
quizzer should use effective memorization techniques.
Some possible ideas include:
- asking items in a random order
- telling the correct answer after the user answers incorrectly
- asking items more often if they were answered incorrectly
- allowing users to configure time limits, so they can
  compare results between quizzes.

Notice that we try to keep our purpose documentation as brief and simple as possible. This is important for all documents, but is especially important for high level docs. There is a basic rule: documentation that is not brief and clear will not be read. You want to keep your docs readable, otherwise they will only weigh down your project.

Exercise 1:

In your README.md, break down the purpose documentation above into some high level requirements. Then give a high level specification for how you would approach those requirements. What programming language would you use? What libraries would you use? What would be your overall approach to each problem?

Exercise 2:

Assume your program user interface would be over the command line. What kind of arguments and user configuration would you accept? Would you let the user use only one quiz file at a time, or use multiple of them? Write down your answers.

Exercise 3:

Skim through the markdown format specification. Markdown is a great format for keeping docs, since it allows you to write docs in plain text (so they can be revision controlled) but the simple formatting rules render beautifully on sites like github.

Markdown is easy to learn, easy to read and has become the defacto standard for writing docs for open source projects. It is worth learning!

High Level Design

Once you know the purpose of your project, it is important for you to write down the approach you plan to take. This is important because:

  • There may be gaps when you don't work on your project. If you go on vacation for a month, having a reference of your thoughts at the time you were focused can jumpstart your productivity.
  • It is important to be able to reference a design doc for new contributors and newbie developers.

Your high level requirements should go in your README.md, just below your purpose section:

# Execution Method
The minimum viable product shall be a command line utility
that is given the path to one or more question files as
arguments

Additional arguments will include:
- `-t`: specify the time allowed for each question
- `-T`: specify the total time allowed for the whole quiz
- `-r NUM`: repeat questions only a certain number of times.
    By default there is no limit

The program will ask one question at a time, recording how
many answers the user got correct/incorrect and weighting
future questions accordingly.

When the program is complete it will report:
- time taken, broken up by whole quiz and each question
- the user's score


# Final Results
When the program is complete a report shall be printed with:
- time taken, broken up by whole quiz and each question
- the user's total score
- questions ranked by ones the user had the most difficulty


# Question File Format
The user shall be able to easily configure the quiz
questions through a simple csv format consisting of two
columns: the question and the answer.

Again, just like the purpose documentation, this documentation aims to be brief and help you during your design process.

Exercise 1:

What are some other items that we can detail at a high level? Try writing them out yourself in this section.

Vocabulary

It is time we briefly discuss design vocabulary.

Specifying meaning

It is very important that your design documents have clear meaning. Without clear meaning, it is difficult to know your definition of done.

  • Shall – Requirement: Shall is used to indicate something that is contractually binding, meaning it must be implemented and its implementation verified. Don’t think of “shall” as a word, but rather as an icon that SCREAMS “This is a requirement.” If a statement does not contain the word “shall” it is not a requirement.
  • Will - Facts or Declaration of Purpose: Will is used to indicate a statement of fact. Will statements are not subject to verification. "The application will be written in python" is an example. Will statements are meant to be notes to inform design or specify implementation details.
  • Should – Goals, non-mandatory provisions: Should is used to indicate a goal which must be addressed by the design team but is not formally verified. Why include should statements? Because you may have a very important issue that you want to communicate to the developers, but can’t think of a way to do so in the form of a verifiable requirement. We have already seen an example in our purpose statement: "the flash quizzer should use effective memorization techniques". There is no way to validate that we are using the best methodologies, but we should aim for that goal.

Reference: these definitions are modified from an article at reqexperts.com (October 9th, 2012. Lou Wheatcraft)

Exercise 1:

Review the documentation we've written so far. When did we use "shall", "will" and "should"? Did we use them correctly?

Testing your software

There are three main categories of testing every developer should know:

Exercise 2:

Read at least the intro for all three wikipedia links above.

Testing Methodologies

The above specify which pieces of your software should be tested, these specify how to determine what tests to write.

Functional Testing

What is the functionality we are programming for? It is important that we test at least the basics. This includes the:

  • boundary conditions: test the extreme inputs and outputs of your function
  • typical use cases: test how the function or application will typically be used (i.e. a few values in the middle of the boundary conditions)
  • error cases: make sure it throws an exception when invalid inputs are used. This should also test that your application can recover in case of recoverable faults.

White Box Testing

Look at your code. As the programmer, what kind of inputs are YOU concerned about? Spend some time focusing on these.

You as the developer have the most insight into the internals of your application, so you are probably the most qualified individual for trying to break it. Always observe Murphy's law: what can go wrong will go wrong. If you can see something that might break, even if the scenario seems impossible for a user to hit, make sure it doesn't break anyway.

Risk Based Testing

What is the worst thing that your function/application could do. Can it segfault? Can it recurse infinitely? Can it delete user data? Could it crash the whole operating system? Can it introduce security vulnerabilities?

It's important to ask what the worst case scenarios are and test for them. This is especially true if your program overwrites files or exposes a port to the internet -- data loss and security vulnerabilities are serious problems that can be introduced in the most simple software.

Tools

Just like revision control best practices would be meaningless without a revision control tool like git, design best practices are meaningless without a tool to help you track your design documents.

Okay, that isn't quite accurate, but it's inaccuracy is the root of why design tools have languished so much. It is easy to think that your requirements can be completely captured in your README, or in code documentation, or in unit tests -- but in the end they get captured everywhere and nowhere. You end up either repeating intention or forgetting to specify it all together.

Let's get this out of the way first: except for some of the simplest libraries, developer documents should not be your only design documents! The things your users are concerned about should almost never be the implementation details or architecture of your project, and unless your library is so simple that all design decisions fit in a few sentences, your code documentation won't cut it for preserving intent or pointing out the relationships between components.

This is not to say that user (README) and developer (code) documentation are not critical to a project's quality: they absolutely are. But they should not replace your requirements, detailed-design or testing documents for any but the simplest projects.

Keep in mind that it is always best practice to try and split your project into as many small libraries as possible. Simplicity should always be the number one goal. However, not all applications and libraries can be made so simple, and even in such cases you still might want an overall architecture for how to achieve such simplicity.

So, here are the primary tools that every developer should have under their belt to increase the quality of their software and their productivity.

Revision Control

This is probably the most essential tool to learn how to use, as it allows you to code fearlessly without worrying that past efforts will be lost. We will be using git in this tutorial to track our progress at each stage.

Inline Documentation Renderers

Most languages can take inline documentation on your functions, classes, etc and translate them into rendered and beautiful user docs. Some languages support it natively, others require a separate library.

We will be writing python documentation can be natively viewed on the REPL by calling help(obj) and could be converted using Spinx.

Unit Testing

Almost every language has a unit testing framework. Get to know yours intimately. We will be using python's unittest package to write tests in this tutorial and pytest to run them.

Linters and Code Formatters

When all code is automatically formatted and linted, it is easier to read and edit. Also, arguing over whether to use tabs or spaces is probably the least productive thing possible. Use the default formatters and linters for your language, it is an easy way to make your code cleaner and simpler.

We will install these in a later chapter.

Custom Tooling, Build Systems and Scripting Languages

Some people believe that the primary responsibility of Software Engineers in Test (my job title) is to design test cases. In my opinion, the primary job of an SET should be to develop processes, tools and frameworks that make the entire organization run more efficiently, more simply and with higher quality.

This section will be one of the least discussed in this book, but it can be the most important for your future development. The problem is that it can't really be taught except to say: always look for ways you can automate pain points and increase simplicity. Develop tools that make it easy to interface with your product, or hook into annoying legacy test software, or develop tests in a simpler way. These tools can be a script, a new way of doing things or even just a library/module. By making everyone's life easier, you will drastically increase quality

artifact: the design doc tool made for developers

Artifact will be our bread and butter for writing and linking our design to our source code and then hosting them as a webpage. This tool was designed specifically to facilitate the philosophy of software quality presented in this book. Artifact will be the topic of the next chapter.

At the end of the process, we will be able to host our design docs just like the artifact project's design docs.

Get Started With Git

Before we do anything else, let's start using git to track our progress. Since artifact tracks design docs which are written in plain text, you can (and should) use revision control to track the evolution of your design documents.

Note: you should have already set up your project

git add README.md
git commit -m "add README.md"

This will add the progress you have made so far to git and allow us to track our progress.

Exercise 1:

git is a fantastic tool, but it is beyond the scope of this guide to give a full tutorial on git or revision control in general. Before proceeding with this guide any further, it is recommended you go through the git tutorial

You can always type git COMMAND -h to get help on any command.

The rest of the tutorial will assume you have a working knowledge of git commands and what their purpose is.

Artifact Intro

This is an introduction to how we start integrating the Artifact tool into our design and development workflow. It is intended to be interactive, so please follow along with everything installed!

Exercise 1: ensuring your environment works:

You should have at least done the Starting Project chapter before attempting this one.

Run art ls, you should see something like:

spc% tst%  | name         | parts
0.0  0.0   | REQ-purpose  |

Converting our README.md into an artifact.

The first thing we want to do is use the README.md file we have already been writing as our artifact file.

To do this, let's make a couple of changes:

  • Move our README.md into design/purpose.md
  • Clean up the headers so they are artifacts.

To move your README.md, simply type:

mv README.md purpose.md

Check In: run art ls. It shows nothing because we have not specified any artifacts.

We now need to convert our headers into artifacts. Let's start with our purpose. Change the # Purpose line (from Specifying Your Purpose) to # REQ-purpose. Your file should now look something like:

# REQ-purpose
Write a flash card quizzer ...

Do the same thing to your specifications from High Level Design:

  • # Execution Method -> # SPC-cli
  • # Final Results -> # SPC-report
  • # Question File Format -> # SPC-format

Now art ls should show:

$ art ls
spc% tst%  | name         | parts
0.0  0.0   | REQ-purpose  |
0.0  0.0   | SPC-cli      |
0.0  0.0   | SPC-format   |
0.0  0.0   | SPC-report   |

This is closer, but notice that none of them are linked. Let's fix that.

For SPC-cli make it look like this:

# SPC-cli
partof:
- REQ-purpose
###
The minimum viable product ...

Do the same for SPC-format and SPC-report, also making them partof REQ-purpose. You should now have:

$ art ls
spc% tst%  | name         | parts
0.0  0.0   | REQ-purpose  | SPC-cli, SPC-format, SPC-report
0.0  0.0   | SPC-cli      |
0.0  0.0   | SPC-format   |
0.0  0.0   | SPC-report   |

Now is also a good time to run art serve. This will serve your project locally so that you can view and edit it through the Web UI.

Here is an example of the project in its current state

Detailed Design

Now that we have our high level design, let's start desiging how we are actually going to build our flash card application. The first thing we might want to design is: how does the user specify questions?

We already answered this to some degree in SPC-format. Let's expand it a bit.

# SPC-format
partof:
- REQ-purpose
###
The user shall be able to easily configure the quiz
questions through a simple csv format consisting of two
columns: the question and the answer.

The format of the csv file **shall** be a csv file of the form:

    City, Capitol

> Note: whitespace will be ignored

## [[.question]]
The `Question` class shall be the primary datatype used for questions in the
application. Quetions shall:
- Store the question and answer.
- Provide a method `ask` to ask the user the question
  and validate the answer.

## [[.validate]]
Input questions **shall** be validated to:
- Guarantee against duplicates.
- Guarantee that the data format is correct.

There are a few things here, so let's take a look:

  • We expanded how the user can configure the questions (the CSV format itself)
  • We created two subartifacts, .question and .validate.
    • Having the [[.subart]] anywhere within the text is enough to create these.
    • We can now link these subarts in code and they are necessary for our artifact to be considered "complete".

Define Tests

Let's also define a couple of unit tests. You could do this using a new TST-format (or any other name) artifact.

However, artifact has what are called tst-subarts specifically for the purpose of defining unit test coverage that you want. Simply add the following section to SPC-format:

## Unit Tests:
- Invalid: make sure invalid inputs don't work
  - [[.tst-invalid_cols]]: Test invalid number of columns (0, 1, and 3).
  - [[.tst-duplicates]]: Test duplicate names.
- Make sure loading works.
  - [[.tst-basic]]: Loading a raw string and validating it.
  - [[.tst-load]]: Loading a valid csv file path and validating it.

These will allow us to implement testing for SPC-format without having to create new artifacts.

Implementation

Now that we have a basic design and test strategy, let's start writing some code.

First make our python module:

mkdir flash/
touch flash/__init__.py
touch flash/load.py

This creates our python module and get's us started with some empty files. Let's first implement SPC-format.question as an object in flash/load.py:

#!/usr/bin/python2
'''
csv loading module
'''
import csv

class Question(object):
    ''' represents a question and can be asked

    partof: #SPC-format.question
    '''
    def __init__(self, question, answer):
        self.question = question.strip()
        self.answer = answer.strip().lower()

    def __eq__(self, other):
        if not isinstance(other, Question):
            return False
        return self.question == other.question and self.answer == other.answer

    def __neq__(self, other):
        return not self == other

Note that we do not have the ask() method yet though (we will do that later).

Now let's implement SPC-format.validate:

def validate_questions(questions):
    ''' Given a list of questions, validate them according to spec
    partof: #SPC-format.validate
    '''
    # check for duplicates
    all_qs = [q.question for q in questions]
    seen = set()
    duplicates = []
    for q in all_qs:
        if q in seen:
            duplicates.append(q)
        seen.add(q)
    if duplicates:
        raise ValueError("duplicate questions found: {}".format(duplicates))

Finally, let's implement SPC-format itself -- the loading of the file.

def load_io(f):
    ''' load questions from a file '''
    reader = csv.reader(f)
    questions = []
    for row in reader:
        if len(row) == 0 or (len(row) == 1 and not row[0].strip()):
            # skip if the row contains nothing but whitespace
            continue
        if len(row) != 2:
            raise ValueError("row is invalid length of {}: {}".format(
                len(row), row))
        questions.append(Question(*row))
    return questions


def load_path(path):
    ''' given a path, load a list of validated questions
    partof: #SPC-format
    '''
    with open(path, 'rb') as f:
        return load_io(f)

When we've finished with all of that, type art ls... and nothing is implemented. This is because we still need to tell Artifact where we have implemented stuff. Edit .art/settings.toml and add "flash/" to the code_paths list.

code_paths = ["flash/"]

Now try:

$ art ls                                                                                                                                                                 ~/tmp/learn-art
spc% tst%  | name         | parts
20.0 0.0   | REQ-purpose  | SPC-cli, SPC-format, SPC-report
0.0  0.0   | SPC-cli      |
60.0 0.0   | SPC-format   |
0.0  0.0   | SPC-report   |

And notice that SPC-format is partially impelmented!

Implementing Unit Tests

In order for SPC-format to be completely implemented and tested we need to impelemnt it's tst- subarts

Note: tst- subarts contribute to both tst% and spc%, the idea being that implementing your unit tests are necessary to being actually done.

Let's implement some tests. First create our test files:

mkdir flash/tests
touch flash/tests/__init__.py
touch flash/tests/test_load.py

Then write our tests in flash/tests/test_load.py:

import os
import unittest
from StringIO import StringIO

from .. import load

script_dir = os.path.split(__file__)[0]


class TestLoadIo(unittest.TestCase):
    def test_basic(self):
        """#SPC-format.tst-basic"""
        text = '''\
        one,1
        two,2
        three,3'''
        result = load.load_io(StringIO(text))
        expected = [
            load.Question("one", "1"),
            load.Question("two", "2"),
            load.Question("three", "3"),
        ]
        self.assertEqual(result, expected)

    def test_csv(self):
        """#SPC-format.tst-load"""
        path = os.path.join(script_dir, 'example.csv')
        result = load.load_path(path)
        expected = [
            load.Question("foo", "bar"),
            load.Question("forest", "ham"),
            load.Question("I", "love"),
            load.Question("you", "too"),
        ]
        self.assertEqual(result, expected)

    def test_invalid_columns(self):
        """#SPC-format.tst-invalid_cols"""
        # extra ',' after 1
        text = '''\
        one,1,
        two,2
        three,3'''
        with self.assertRaises(ValueError):
            load.load_io(StringIO(text))

    def test_duplicate(self):
        """#SPC-format.tst-duplicates"""
        # note: extra ',' after 1
        text = '''\
        one,1,
        two,2
        three,3
        two,2'''
        with self.assertRaises(ValueError):
            load.load_io(StringIO(text))

    def test_valid_line_ending(self):
        """The last line should be able to end with '\n'."""
        text = '''\
        one,1
        two,2
        three,3
        '''
        result = load.load_io(StringIO(text))
        expected = [
            load.Question("one", "1"),
            load.Question("two", "2"),
            load.Question("three", "3"),
        ]
        self.assertEqual(result, expected)

Notice that we also have an extra unit test. That's okay, not every test needs a coresponding spec in the real world either!

Summary

We have successfully implemented and tested one artifact (SPC-format), along with all of its subarts. We did this by implementing it in source code.

View the current status using art serve. You can also see the example here.

Notice that SPC-format is considered both spc% and tst% complete and so is green. Furthermore, when you look at it you can see the lines where it is implemented.

Cleaning Up

There are two more commands that it is critical to know:

  • art check: for checking for errors and warnings.
  • art fmt: for formatting your arguments.

art check checks a whole range of things:

  • All artifacts in partof exist.
  • The soft references (i.e. [[REQ-foo]]) exist.
  • Code references are valid and not duplicated.

art fmt standardizes the format of your artifacts and makes them easier to read.

Note: art fmt is automatically run whenever you edit any artifacts via the Web UI.

Documenting and Hosting your own project

To start documenting your own project, run art init in your project and edit .art/settings.toml with the paths on where to find your design docs and code.

Have your build system export your design documents as html for easy viewing. See: Exporting Html

Artifact Advice

Here are a words when using artifact:

  1. You should always write a good README and other documentation for your users -- design docs SHOULD be used for bringing developers of your project up to speed but they aren't the best format for general users.
  2. Keep your design docs fairly high level -- don't try to design every detail using artifact. Using artifact does not mean that you shouldn't use code comments!
  3. Use art ls and art check often, and fix those error messages!
  4. Follow the artifact best practices.
  5. Don't be afraid to refactor your design docs. It is actually easier than it might sound, as the tool will help you find broken links and incomplete items in real time. Not to mention that if you use revision control (you should), your artifacts can be tracked with your project -- no more having your documentation and your code be wildly out of sync!

This tutorial took you part of the way through developing a simple project using artifact. Continue onto the next section or simply try using artifact for one of your smaller personal projects and see the benefits that design documents can give -- it's your choice!

Have some fun with the tool, try to break it. If you find bugs or have any suggestions, please open a ticket at: https://github.com/vitiral/artifact/issues

Good luck!

Finishing The Project

This is a continuation of the last section and is still a work in progress. We are going to flush out the design for the rest of the flash challenge and implement an MVP.

Running Tests

If you followed along with the artifact interactive tutorial, you should feel pretty confident by now that our load component is well designed and should be implemented and tested. However, you haven't actually run any code yet, so you can't be sure! We are going to change that.

The first thing you need to do is make sure you are running python2.7. Running:

python --version
pip --version

Should return something like:

Python 2.7.13
pip 9.0.1 from /usr/lib/python2.7/site-packages (python 2.7)

As long as they are both python2.7.X (but not python3.X), you are good to go.

If not... python can be very difficult to configure. Search on google for how to have both python2 and python3 installed. You will have to do a similar exercise for pip.

If it is too much of a pain, you can also just use python3 (or any other language), it shouldn't be difficult -- you will just have to fix any errors that come up.

If you are using another language, refer to that language's unit testing guide.

Now install py.test:

pip install pytest

Note: it is recommended you add --user to the end or use a virtualenv. Using a virtualenv with python is out of scope of this tutorial.

Now run your unit tests:

py.test flash/

Congratulations, you've designed, written and run unit tests!

TODO

The rest of these documents are not yet complete. Sorry for the inconvieneice!

Addendum

This section contains the addendum to this guide, including the spec, FAQ and other materials.

Why is it named artifact?

Artifact is simply named after what it does: it is a way to write and track your artifacts

Why is (extended) markdown the default language?

Because it is human/readable and writeable. Adding the metadata block was also not difficult and fit within the syntax.

An artifact is "implemented" in code but not 100% done?

All artifacts are only as done as their parts + implementation/done.

If you have:

[SPC-1]
[SPC-1-a]
[SPC-1-b]

And then the code:

def hello():
    """partof: #SPC-1"""

SPC-1 will only be 1/3 "done" since it still has two incomplete parts.

This also applies to the "done" field.

These are a few of the (alpha) best practices when using artifact.

use-features: use the features of artifact

Artifact contains several useful features. Use them!

  • art check: makes sure your references are all valid both in code and in your artifacts.
  • art fmt: format your artifacts

too-many-pieces: Do not break into too many pieces

Artifact is a great tool for breaking up your design, so much so that it is tempting to specify every detail as it's own artifact

Avoid this:

  • SPC-ui-cmd
  • SPC-ui-cmd-ls
  • SPC-ui-cmd-fmt
  • SPC-ui-web
  • SPC-ui-web-list
  • SPC-ui-web-edit
  • SPC-ui-gui
  • ... etc...

There is no reason to have the ui prefix -- each of these are almost completely different components and doesn't aid in understanding your design documents.

Instead, consider having:

  • REQ-ui: high level ui requirements
  • REQ-cmd: partof=REQ-ui
  • REQ-web: partof=REQ-ui
  • REQ-gui: partof=REQ-ui

This keeps the breakdown obvious but also keeps the names short.

short-names: Keep artifact names as short as possible

Nobody wants to read REQ-ui-web-design-frontend-edit-text. That level of nesting is simply not necessary. Something like REQ-web-edit will suffice.

Try and combine such detail into a single artifact. Auto-linking is cool, but don't get carried away! It's okay to specify partof when it makes your tree simpler.

no-numbers: Use only human readable names

Artifact names should avoid using numbers. If you are tempted to call something SPC-foo-1 just break down the different items of foo in a bullet point list in its text field and use subnames.

abbreviations: abbreviate names

Artifact is intended to be used as a cmd line tool, so keeping names short is very nice.

This is mostly useful for larger projects.

prefix-acronyms: create acronyms for your prefixes

Use an acronym or abbreviation for your prefixes.

One of the main use cases of short names is for the categories of your artifacts. For instance, say your storage product had the following features:

  • transport
  • data availability
  • data recovery
  • remote replication

It would be annoying to have artifacts like REQ-transport-drive or REQ-remote_replication-protocol. Instead, use an acronyms:

  • TP: transport
  • DA: data availability
  • DR: data recovery
  • RR: remote replication

Now your artifacts look like REQ-TP-drive and REQ-RR-protocol, which is much shorter and more readable when looking at a large list.

uniformity: keep names uniform

Artifact automatically makes SPC-foo a partof REQ-foo and that is because they should be related. Make sure your names have meaning so this doesn't accidentally become a gotcha for your project.

The art export html $DEST command can be used to create a static site which is included on github. For an example, see artifact's own design docs

In order to make a github page for your site that hosts your design documents:

  • Activate github pages (we will be using the index.html option)
  • Run art export html, which will generate an index.html file among other necessary files and folders.
  • Run git add index.html css to add the generated files.
  • Push to master or to gh-pages branch.

That's it! You should be able to navigate to http://<username>.github.io/<repo-name>/ to view your page!

Reporting an Issue

If you have any issues with artifact, please report a bug at it's issue tracker.

Feedback on Artifact

Below is feedback I have compiled on artifact so far. If you would like to leave feedback please open a ticket, contact me on twitter @vitiral or on reddit.

  • omniaVincitVeritas, June 2017: I like this idea a lot. Our profession has a real problem with massively overengineered architecture tools, but artifact is like fresh summer rain.

Artifact Document Specification

This document outlines the specification for the Artifact data format. All specifications in this document are released as Creative Commons CC0 public domain. You can read more about this license here: https://creativecommons.org/publicdomain/

Document Type

Artifact documents can be specified in multiple formats.

TOML Format

The TOML format adheres to a subset of the TOML format and are documents of the form:

[ART-baz]
partof = "ART-baa"
text = '''
multi-line
description
'''

[ART-foo-bar]
partof = [
    "ART-baz",
]
text = '''
multi-line
description
'''

Where partof can be either a single string or a list of strings.

Markdown Format

The markdown format uses extended Commonmark (TODO: link) format. It is of the form:

# REQ-foo
<optional yaml section for metadata>
###
<markdown text>

Where the yaml section is completely optional (the ### can be skipped if it doesn't need it.

Artifact Types

Instead of ART as defined in Document Type, the user must select from 3 artifact types:

  • REQ: specifying a requirement. REQ can only have REQ in its partof field.
  • SPC: specifying a design specification. SPC can only have REQ or SPC in its partof field.
  • TST: specifying a test of a SPC. TST can have any of REQ, SPC or TST in its partof field.

Automatic Links

The following will be automatically linked:

  • parents: REQ-foo will automatically be a partof REQ-foo-bar
  • common-prefix for REQ -> SPC -> TST links
    • REQ-foo will automatically be a partof SPC-foo if REQ-foo exists
    • SPC-foo will automatically be a partof TST-foo if SPC-foo exists

Linking an artifact in source code

Artifacts can be linked in source code, which "completes" their spc%.

The way to link to an artifact is to place #ART-name anywhere in the source code file.

Sub Artifacts (subart)

A sub artifact is defined by placing [[.subart]] anywhere in the text field of an artifact.

Subarts can be linked in source code as well by placing #ART-name.subart anywhere in the source code file.

A special subart is [[.tst-subart]] which will contribute to both tst% and spc%.

Artifact Documentation Directory License

All documents in this directory are released under the CC0 Creative Commons Public Domain License with the intent that you should feel free to copy, paste and modify any of the designs, guides or examples for any purpose. You can read more about CC0 here:

https://creativecommons.org/publicdomain/