This page lists some Best Practices that you should be aware of. They make it much easier for developers to cooperate.
Talk to the community.
Elody / The Legion of Devs is very much a communal project.
Use the forum to organize and discuss things before making larger changes. Who knows, maybe someone can give you tips that will save you a lot of time?
If you are new, head here and introduce yourself!
Build on what already exists instead of adding something completely new.
Before adding a feature, check if something similar already exists. Simply run a Scenario in Developer Mode and enter whatever information should trigger the Rule/Program you want to add. Check what Tags and Symbols are created. Can you use any of these for your own Rules?
For example, if you want to trigger a Program when certain keywords are mentioned, it is a good idea to rely on an existing Natural Language Program as a preprocessor instead of checking the user's input directly. This way your Rule will also be used if related keywords are mentioned that you might otherwise overlook.
Review each others' work.
Be aware that Elody will not use Rules you define until enough people have rated them and confirmed that they are useful. For this reason, it helps if everyone in a group looks over each others' work and rates it, to promote it to prominence more quickly.
Always add documentation to your Rules and Programs.
Make use of the [[Program/Rule/symbol:name/id]] pattern to create links to other Programs/Rules/Symbols. Other developers will thank you for the convenience.
Mention the strengths and weaknesses of your code, i.e. what are you doing that others missed, and what special cases still need to be handled.
If you are a team:
- Create a thread on the forum where you can discuss things with your teammates.
- Create a Scenario Plan where all your team members are marked as trusted. This will allow you to test each others' contributions.
- Once you are satisfied with your work, have every member of the team (and anyone else you can convince) rate all of the Rules you created to make Elody trust them.
Speed things up with a proper testing environment
Contributing to Elody gets a lot easier and faster if you set up your programming environment properly:
Set up a Scenario Plan that automatically sets up everything the way you want it. This way you can run a test with just one click.
If your program is particularly complex, it can help to create a simple dummy program and a rule to run it, which does nothing but prepare the Scenario for what you actually want to test.
If a program does something wrong when it is run as part of Elody, you can switch to running it locally until you have fixed the bug, so you don't have to upload it again while you are debugging.
The command 'test-program' needs the location of the folder you want to rerun. You can get this quickly: The directory is <root>/exec<scenario_id>/step<step_number>, where the root is whatever you configured your lod-executor to use, the scenario_id can be found in the url of the active Scenario, and the step_number can be found by entering Developer Mode and checking in which step the program was executed. All of this is pretty quick to find, so testing doesn't take much time if you know where to look.
When you use 'test-program', a log file will be created in the execution folder. Any print statements in your program will be automatically redirected to that file. You can also use the '-l' option to see those print statements directly in your console.
To make debugging easier, you can add a Symbol like debug_mode to your Scenario Plan and make your program generate Messages for Elody or print statements to the log file if this Tag exists.
You are heavily encouraged to name your objects according to the following scheme, to make things easier to understand for other developers.
Symbols are named with underscores and start with a lowercase letter: this_is_a_Symbol
Before adding a new symbol, check if something similar already exists. Symbols with duplicate meaning create extra work for contributors!
When choosing names for new symbols, be very specific. Different people come from different backgrounds and use different Programming languages so they can have very different standards.
Elody gives special treatment to Symbols with these prefixes:
Represents a type of task.
These tags always target a task_ tag as their first argument
Elody considers a task_ to be finished if all the require_ tags on it are tagged with !provide.
Used to temporarily reserve an object.
Additionally, the following prefixes are common:
These Tags are supposed to be used to trigger a Demo- Rule directly and have no meaning beyond that.
The kinds of Tags are used to describe the format of a file. The first argument of such a Tag should always be a file.
You can combine this with the prefix require_ to form a 'require_format_' Symbol, which indicates that a file needs to be in a certain format.
Example purposes: Specify that a file is a CSV file. Specify whether or not a CSV file has a header.
Gives information about something. These tags are used to store metadata describing objects and can be used to share information between different Programs.
This is similar to format_, but has a different purpose: format_ Tags are used mainly to make sure that files and objects get parsed correctly, while info_ Tags are used to inform decisions.
Programs are named with underscores and start with a capital letter: This-is-a-Program
Alternatively, they can also be written with capitalized camelcase: ThisIsAProgram
Rules are named with hyphens and start with a capital letter: This-is-a-Rule
Options are named with hyphens and start with a lowercase letter: this-is-an-Option
It is recommended to name Options similarly to the Rules that created them, whenever doing so makes sense. Often it is even the most understandable if you give the Option the same name as the Rule, just without the capitalized first letter.
Variables in Rules and Options
Rules and Options can contain named variables. Use camelCase with a lowercase first letter for them: thisIsAVariable
Prefixes for Rules and Options
Most Rules and Options can be roughly categorized by the purpose they serve. If a Rule or Option matches one of these archetypes, it is a good idea to give its name the corresponding prefix. This will make it easier to understand for other users:
These Rules are supposed to be triggered directly through a corresponding demo_ Symbol. They should not be called in an ordinary Scenario and should only be used when explicitly triggered at the start of a Scenario.
The Rule or Option is used as an intermediary to spawn another Option that does the actual work. Since all Rules are supposed to work like this, using this prefix this is not necessary for Rules. It can be useful for nested Options, though: give the higher level Options a 'spawn-' prefix to make it clear that it acts indirectly by creating another Option.
The purpose of this Rule or Option is to interact with the user.
This Rule or Option waits for a particular condition that may or may not occur and reacts if it recognizes the condition.
A prefix for Options that should be presented to the user so that the user can manually verify something. Similar to 'Interact-'.
A helper that lays the groundwork to make another Rule/Program possible, usually by triggering an Enrich- Rule that is needed to get it to run.
Enriches a Scenario by populating it with additional objects that may be needed.
Generic prefix for when nothing else applies.
Presents information to the user. Often the last step of a task.
This is used when fixing someone else's Rule the hard way. It is not cooperative, and not recommended if an alternative exists. If you have to do this, make sure to contact the author of the Rule you are fixing and tell them about it, so that they can fix it themselves and the Hotfix- Rule can be deleted. The Options generated by a Hotfix- Rule get the prefixes 'undo-' and 'hotfix-' for undoing the mistakes of the orginal and for making things right, respectively.
Used for Options that are supposed to run immediately after another event is finished, most often a Program execution event.
Used for Options that perform clean up operations once some conditions are met.
Symbols have meaning. Try to make that meaning clear in the description.
If it is not clear what your Symbol is supposed to represent, other developers may misuse it and that could make both your Rules and theirs less effective.
While you are able to change the description of a Symbol later on, this is intended for small changes. Don't change the way Symbols are meant to be used when those symbols are already in use by other developers. Try to coordinate with others to avoid problems with redefining symbols. If no consensus can be reached, it may be better to create new symbol than to alter existing ones.
It's better to have too many Tags than too few.
Tags can be used to describe things, to connect things, to activate or suppress Rules, and for virtually any imaginable other task. Try to express as much information as possible with Tags. You never know when something you consider unimportant might be useful for another developer.
To make it easier for Rules to cooperate with each other, explicitly define tasks with requirements whenever doing so makes sense.
To define a task, use a Tag who's Symbol begins with 'task_'.
The purpose of a task is to make sure that Rules and Options can be used multiple times in the same scenario and don't get confused about the objects they should use as arguments.
Tasks can be independent of each other, or form a directed acyclic graph. Presumably most users will only have one concrete task and start a new Scenario for the next task, but you never know when someone might upload code that builds on your tasks to solve a more complex task.
Every time the user wants to do something new that is independent of what he wanted earlier, create a new task_ Tag.
Every time a task requires multiple subtasks of the same kind, create a new task for each of them.
If a task has subtasks, but only one of each kind, then there is no risk of confusion. In this case, explicitly creating a subtask is Optional. This is the case in the example, where require_ and !provide are used for each of the subtasks, but no subtasks are created explicitly.
You can use Tags to describe the content of objects, even if those objects can be changed.
Since the arguments of a Rule or Option will by default always look for the last object that matches the filter, updating/overwriting these descriptive Tags is easy: You can in most cases simply attach a new version of the descriptive Tag, without having to !nullify any of the old ones. Rules and Options written in the default way will automatically grab the newest version.
Rules and Options
Use Options as intermediaries
You can always choose between having your Rules do things directly, or creating Options as intermediates. If you go the first route, then the consequences of your Rule can't be interrupted or suppressed once it has been selected for execution. This may seem like a good thing at first, but it isn't.
If your Rule ever does something bad, and nothing can interrupt it, then it will receive negative feedback. Eventually, Elody may decide not to use your Rule anymore because it causes problems too often, even if it is normally useful.
On the other hand, if your Rule acts indirectly by creating an Option, then other Rules can suppress or interrupt it when necessary. As a result, your Rule will still be used when it does something useful, but it will be suppressed when it would do something harmful.
Rules should be independent entities.
If another Rule that your Rule relies on has a too low rating to run, make sure that your Rule does not do anything stupid. If possible, make it fail gracefully and maybe create a Message (visible only to developers) in which it mentions that another required Rule did not get loaded by Elody.
If it is not possible to do this at all, consider merging both Rules into one.
When you set the confidence of an Option, follow these guidelines:
- <0.5: Probably wrong. Only display these Options if nothing else works. (0.5 is the default threshold for ignoring an Option vs. displaying it to the user.)
- 0.5-1.0: Probably good, but better to ask the user first. (1.0 is the default threshold for displaying an Option to the user vs. executing it immediately.)
- 1.0-2.0: Most likely useful. Execute this Option without asking first, unless the user has high security standards. (2.0 is the maximum threshold value for displaying vs executing that a user can select)
- 2-999: These Options should always be executed, but the ones with higher confidence execute first. Choose the confidence based on what confidence values other Options use.
- 1000: The confidence here is a formality. The only important question is whether or not the trigger matches, and the code here is only put in an Option to give other Rules a chance to !deactivate them. These Options should otherwise always execute. Most Options generated directly by Rules get this confidence.
- 9999: postprocessing- and cleanup- Options. Their confidence is extremely high to ensure they run without delay once their trigger's condition is met.
If two Rules are direct competitors with each other but cooperative, they should give the Options they create the same confidence. If two Options have the same confidence, the one that was created first takes precedence. Since the order in which the Options are created depends on the order in which the Rules were executed, this ensures that that the Option from the Rule with the better rating takes precedence.
If one of the Rules is not cooperative (i.e. it creates its Options at a higher confidence than the others), then the way to deal with this is to forcibly deactivate the non-cooperative Rule as demonstrated in the tutorial.
This website is currently in Beta. If you think you know a better way to do things, please tell us about it!
Always use the fields 'repeat' and 'deactivate_if' in the trigger to clean up after yourself.
You should check that all Options related to a task are deactivated as soon as they are no longer needed. This removes clutter and makes the control flow much easier to understand. You can use the buttons labeled "Show status overview" on each Step in the Developer Mode to check which Options are currently alive and to backtrack where they came from if you are unsure of their purpose.
When you make changes to your programs, make sure that those changes only affect your own tests and not other users until you are ready to release the changed version of the Program to the public.
You can use the '-r' option when uploading a Pogram to mark it as a release version. You can use the Tag !default_program_version with the #dev option to use the development version of the Program in your own Scenario Plan (simply create a !default_program_version Tag as a starting tag in the Scenario Plan).
Be careful with your runtime.
It costs some time to start a Program, because each Program runs in an isolated environment for security reasons. It is therefore faster to solve multiple problems with a single call to a Program than to call the same Program multiple times with different parameters. On the other hand, solving many different kinds of problems with the same Program can get quite confusing. Be mindful of both issues and try to find a good balance.
This website is still under development. The speed of executing a Program and the overhead of starting a new Program will be improved as the technology matures.
There are some guidelines to consider when rating a Rule or Program, or when creating feedback request forms so that other people can rate them.
Don't lie about responsibility.
It is possible to create Feedback Request forms for Rules and Programs that aren't yours. This is a deliberate feature since it can somestimes be useful. Do not abuse it to make misleading feedback requests. This is grounds for the deletion of a Rule or Program even if it otherwise works very well.