title | author | origin | date | |
---|---|---|---|---|
Preventing Errors |
Peter Steinbach |
Max Planck Institute of Molecular Cell Biology and Genetics, Dresden |
steinbac@mpi-cbg.de |
November 7 , 2016 |
int i = 1;
. . .
**any more code might introduce errors!** ![](img/heise_artikel.png)-
Good Code prevents Errors
-
Redundat Code
-
Dynamic State
(George A. Miller; The magical number seven plus or minus two; Some limits on our capacity for processing informations; Psychological Review 63 8 (1956), S. 81–97)
- where to apply change
- change propagation (dependencies)
- requirements to consider when applying a change
- answering emails
- answering walk-in requests
- pressure of deliverables
- keyboard short-cuts, menue structure
- syntax to use (programming language, programming paradigms)
- library semantics and API
- academic context (time and hierarchy pressure)
- scientific context (what problem do I wanna solve?)
- manage your time (e.g. Pomodoro, Getting-Things-Done)
. . .
-
keep your head free to solve the problem
-
no extensive working hours
-
use leisure to relax (not to work)
-
. . .
-
Test-Driven Development (TDD)
- write failing test for feature (red)
- add implmenentation and make test pass (green)
- Refactor to achieve good design while all tests pass (refactor)
- return to 1.
Robert C. Martin, "Clean Code: A Handbook of Agile Software Craftsmanship", 2008, Prentice Hall
- naming a variables/class/function in a readable fashion, improves system design
- requires exercise and discipline
- can't find a good name?
. . .
Maybe your mental model is defective or not complete!
. . .
- can't change the variable/function/class name in a project easily?
. . .
Maybe your editor is not supporting you enough!
- helps compiler to find bugs before they happen
- may induce boiler plate code (i.e. code necessary for the compiler only)
- good type system helps to construct application logic with domain-specific types so that unrequired states lead to compiler errors
- example: special case handling with null references
case class Person(name: String, email: String)
trait Newsletter {
val persons: List[Person]
def sendNewsletterTo(email: String): Unit
def sendNewsletter: Unit = {
for (person <- persons)
if (person.email != null) //please remember this!
sendNewsletterTo(person.email)
}
}
-
simple example, check for
null
is hard to forget -
in complex real scenario,
null
check can be forgotten easily- defensive profgramming: check state everywhere (if-elseif-else madness)
- problem: code becomes unreadable
-
alternatives:
- use optional data types (yields None and StringType as sub types)
- subclassing: Person class with email, Person class without email
case class Person(name: String, email: Option[String])
trait Newsletter {
val persons: List[Person]
def sendNewsletterTo(email: String): Unit
def sendNewsletter: Unit = {
for (person <- persons)
person.email foreach sendNewsletterTo
}
}
-
easy and quick fix
-
a lot of technical debt accumulated
-
future change: has to be propagated to many places
-
don't copy&paste, but abstract
- minor changes should induce new functions/subclasses
- in practice often doable but hard
def getNumPersons(query: PreparedStatement): Long = {
val results = query.executeQuery
try {
results.next
return results.getLong("numPersons")
} finally {
results.close
}
}
def getNumPersonsNamed(
name: String, query: PreparedStatement): Int = {
query.setString(1, name)
val results = query.executeQuery
try {
results.next
return results.getInt("numNamedPersons")
} finally {
results.close
}
}
- Template Method Pattern (GoF Pattern, class architecture for query)
- Higher-Order Functions using lamdas (functional approach)
preventing copy&paste is doable!
-
state base programming from a time, where state was mapped to memory (due to memory being scarce)
-
today: memory not a problem, topics like parallelisation are important
-
dynamic state induces complexity, complexity is hard to test (compare bigger/normal classes to functions)
Avoid state whereever possible!
-
state required in many cases
- i/o
- events from a GUI
- data base access
-
new trends emerge:
- reactive programming
- data bases with event based records (journal of data changes is kept)
- persistent data structures (system knows changes to data, always trackable, e.g. Spark)
-
software development is a cognitive demanding activity
-
techniques exist to reduce complexity
-
by reducing complexity, ease cognitive load
-
less cognitive load, less errors