Custom Bash tab completion for my program

Andy Balaam from Andy Balaam's Blog

I love Bash tab completion, and I want it for the command I am writing, so it can automatically complete parts of the command line when I run my program.

Code

Here is the script (install-bash-completion) I wrote to set it up (no need to be root – it installs in ~/.local):

#!/bin/bash

set -u
set -e

DIR=${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions

mkdir -p ${DIR}

cp _myprogram ${DIR}

The actual completion script (_myprogram) it installs looks like this:

_myprogram_commands()
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts=$(bash -c "./myprogram --commands")

    COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
    return 0
}
complete -F _myprogram_commands ./myprogram

Installing

To install it, run:

./install-bash-completion

Then log out and log in again.

Now when you type ./myprogram and press TAB a couple of times, you should see the possible completions listed.

Notes

The completion script must be named to match the program name, with a leading underscore.

If I wanted it to work when it was installed in my PATH, I would need to change ./myprogram to just myprogram in 2 places.

Notice the line opts=$(bash -c "./myprogram --commands") – it actually runs my program to get the list of completions. This means my program needs to accept a --commands option which prints the valid commands. Alternatively, I could have hard-coded it by replacing that line with just:

opts="cmd1 cmd2 --help --etc"

More info:

The Spectral Apparition – a.k.

a.k. from thus spake a.k.

Over the last few months we have seen how we can efficiently implement the Householder transformations and shifted Givens rotations used by Francis's algorithm to diagonalise a real symmetric matrix M, yielding its eigensystem in a matrix V whose columns are its eigenvectors and a diagonal matrix Λ whose diagonal elements are their associated eigenvalues, which satisfy

    M = V × Λ × VT

and together are known as the spectral decomposition of M.
In this post, we shall add it to the ak library using the householder and givens functions that we have put so much effort into optimising.

Invariants and Preconditions

Anthony Williams from Just Software Solutions Blog

I tend to think about invariants and preconditions a lot. Pretty much every class has invariants, and most functions have preconditions. I don't think they are complicated concepts, but somehow they seem to confuse people anyway, so I decided it was time to write down some of my thoughts.

Invariants

A class invariant is something that is always true for every instance of that class. Users of objects of that class never see an object for which the invariant is not true; it is true for all states of all objects of that class at all times. This is what makes it invariant.

In order to perform the required operations, class member functions may temporarily break invariants, and then restore them afterwards. Unless you allow concurrent invocations of member functions on the same object, or deliberately pass the object to another function from inside a member function, this is OK, as the outside world will not be able to interact with the object when it is in such a state. It does mean you need to be especially careful when calling other functions from inside a member function, to ensure that the invariants still hold.

Invariants are important. If you writing a function that operates on an object, the only thing that is guaranteed to be true about it is that the class invariants hold, unless you choose to impose additional preconditions.

Preconditions

Preconditions are those that must be true when a function is called. Every operation on a class has the implicit precondition that the class invariants hold, but operations can add additional preconditions. For example, you always call v.empty() to check if a vector is empty, but you only call v.front() to retrieve the first element if the vector is not empty. v[i] and v.at(i) differ only in their preconditions; v[i] requires the vector to have more than i elements, whereas v.at(i) is specified to work for any vector, throwing if i is out of range.

Some people like to write classes with two-stage construction — first you default-construct the object, then you call x.init() or similar to "finish off" the initialization, and you can't call any other function on that object until after the initialization is complete. Though I don't like this pattern it is valid code; every member function has a precondition that x.init() has been called.

Some people like to say that such a class can have "invariants" that do not hold until after initialization is complete. I think this is a misuse of the term; invariants must always hold for every object of a class, outside the internals of its member functions. As I just stated above, what such a class really has is a precondition on every function, not an invariant.

If I write a function

void do_stuff(X& x){
  // my code here
}

then I can rely on the invariants of X holding, but cannot rely on anything else unless I add preconditions to do_stuff.

Likewise, it should not be possible to do anything to an object that breaks its invariants. If you can then either you gave a bug, or they are not really invariants, just preconditions for most operations.

Some people like to write classes that have operations to transfer the internals from one object to another, leaving the source in a special "emptier than empty" state. This can be good for efficiency, especially when doing otherwise would require allocating resources to return the source to the plain "empty" state, and it is likely that the source will be destroyed immediately anyway.

Again, this is a perfectly reasonable thing to do, particularly when the resources are expensive to allocate and performance of the code is important. The problem comes when people then want to say that the class invariants don't hold for this state. Just as with the pre-initialization state above, I think this is a misuse of the term; they are not invariants, it is just that most operations have a precondition that the object is not "emptier than empty".

Move semantics

Since C++11, C++ has had the concept of "moving" objects, transferring resources from the source to the destination. This is an important facility for expressiveness of code and efficiency. Objects that own resources such as std::unique_ptr, std::thread or std::fstream can now be moved, transferring ownership of the resource from the source to the destination. This allows such objects to be put in containers, and transferred between scopes, which can greatly simplify code. Likewise, containers like std::vector can be moved, transferring the entire set of contained objects from one vector to another without reallocation. These facilities are of great importance for writing clean, efficient code.

One thing all these operations have in common is that after a move, the class invariants still hold for the source object. This should go without saying: the source is still an object of its type, so of course the invariants hold.

The issue is with things like std::list, where some implementations allocate a sentinel node for the head/tail of the list in the default constructor, which is thus transferred during move operations, so the move constructor has to allocate a new sentinel node for the source, in order to ensure the class invariants still hold. A similar thing occurs with anything that uses the pimpl idiom: if the implementation pointer is transferred then either the class invariants must allow for there to be a null implementation pointer, or a new implementation object must be allocated.

Some people therefore argue that move operations should allow the source to be left with broken invariants, as a hollow shell of an object, in an "emptier than empty" state. I think this is misuse of the term "invariants". By all means, steal the internals and leave the source with a null pointer to the internals, or whatever. However, you now need to change the invariants of your class to allow this, or they are not invariants, because they would no longer apply to all objects of that class. There is no need to update any of the functions on your class to handle this new state, but if you do not do so, then any functions that don't work for objects in this "emptier than empty" state now have an additional precondition that the object is not in that state.

This is all perfectly fine, objects with such states are plentiful, and have legitimate reasons for existing, but it is important to accept that their functions now have preconditions. My do_stuff function above can only call member functions that are safe to use in such a state unless it too has a precondition that the object x is not in such a state.

From a user perspective, it would be nice to be able to query such a state, so I could know what operations are permitted. For example, std::future provides a member function valid so you can call f.valid() before trying to do anything with it, and std::thread provides the joinable member function, which can be used to see if the object holds a thread handle. My do_stuff function can then call x.is_emptier_than_empty() in order to check for the special state and take appropriate action. That said, this is a "nice to have": the absence of such a function doesn't mean that the state can't exist, just that it's potentially harder to deal with.

Interaction with other code

If you pass an object to a function or class, then you need to know what that function requires of your object and ensure that those expectations are met. If the function expects your object to be a container with at least one element and you pass an empty container, then you've broken the expectations and your code has a bug. Likewise, if the function expects to be able to call x.foo() on your object, but foo cannot be called on an "emptier than empty" object, and you passed such an object to the function, your code has a bug.

The difficulty comes when such a state arises as a consequence of other actions. If x.foo() can leave x in an "emptier than empty" state, then do_stuff had better be prepared to handle the scenario, otherwise there is a bug somewhere. Where the bug is depends on the documentation: if do_stuff requires that you can always perform certain actions on the object it is passed as a parameter, and those actions require that the object is not "emptier than empty", then the bug is in the caller, or maybe the implementation of class X. If there is no such documentation, the bug is in do_stuff.

Note that requiring that x.foo() can only be called with objects that are not "emptier than empty" is a precondition. It should be perfectly acceptable for do_stuff to call any functions on x that do not have preconditions, even if x ends up in an "emptier than empty" state.

This is the exactly the same scenario we get with other code. For example if you pass a vector to a function that requires that the vector isn't empty, the function can erase the last element without a problem. If it wishes to erase the last element again, thus removing two elements in total, then it needs to check for and handle the case that the first erasure left the vector empty. Calling v.clear() or v.empty() or v.push_back(y) would be fine without checking, as those functions have no preconditions.

If x.foo() is instead spelled y=std::move(x), then nothing changes: it is perfectly fine for x to end up in an "emptier than empty" state if do_stuff knows how to handle such a state, or doesn't care, because it doesn't touch x again.

One option is for do_stuff to say that after a move-assignment like this, it can't rely on x having any particular value, but it is still an instance of class X, so its invariants must hold, and therefore any operation without a precondition can be used. It can therefore call x.is_emptier_than_empty() and behave appropriately.

The other option is for do_stuff to place stronger requirements on x, such as requiring that it does not end up "emptier than empty" after a move, or even requiring that it has a specific state.

Valid but unspecified states

The standard library has chosen option 1: after move, objects must be valid - i.e. still actually be objects of the expected type, and not destroyed - but they have an unspecified state, so the function cannot rely on any specific value, and can therefore only perform operations without preconditions, until it has verified that the preconditions for other operations hold.

This holds both for standard library types such as std::string or std::vector<int>, but also for user defined types that you use with the standard library. If you write

std::string s="hello";
std::string s2=std::move(s);

then s was the source of a move operation, and thus is in a valid, but unspecified state. For implementations that use the Small String Optimization, such that short strings are stored entirely within the string object itself, without requiring dynamic allocation, this might mean that s still has the value hello after the move, because then it doesn't have to store an empty string value in s as well as copying the contents to s2. It is also possible that the implementation might clear s, for consistency with longer strings. Both are valid choices, as in either case s is still a std::string object, and the exact details of the state are unspecified.

Likewise, if you write

std::vector<MyWidget> v=create_some_widgets();
v.insert(v.begin(),MyWidget());

then that call to v.insert is going to have to move the widgets around in order to make room at the beginning for the extra one. The library requires that moving widgets leaves them in a valid, but unspecified, state. In this case, that means that having moved a widget from one position to another, it is OK to move another widget on top of the first one, as that is the only operation the vector will perform anyway. If you pass your object to a standard library function that does something other than move things around (such as std::sort or std::remove_if), then you need to check that the other operations the function might do can still be done on a moved-from object. By calling the library function you are stating that your objects meet (and will continue to meet) any preconditions you have imposed on the operations that the library function specification says it might perform.

Invariants and Concurrency

Right back at the beginning of this article I stated that "Users of objects of that class never see an object for which the invariant is not true", but also that "class member functions may temporarily break invariants, and then restore them afterwards". These two things don't work together very well if an object can be accessed concurrently from multiple threads, and this is one aspect that makes writing concurrent code hard, especially if you try to use fine-grained locks or atomic operations.

Consider a simple class Names which holds two vectors, one for first names and one for last names:

class Names{
  std::vector<std::string> firstNames,lastNames;
};

I want the invariant of this class to be that the number of elements in firstNames and lastNames is always the same, so that I can add operations to this class knowing that is always true. There is a strong argument that this ought to be a std::vector<std::pair<std::string,std::string>> rather than two vectors, but assume that the class author has a legitimate reason for the separate vectors.

At first glance, a member function to add an entry is quite simple:

void Names::addEntry(std::string const& first,std::string const& last){
    firstNames.push_back(first);
    lastNames.push_back(last);
}

However, even for the single-threaded case, this isn't good: if lastNames.push_back(last) throws an exception, then our invariant is broken, as we successfully added an element to firstNames but not to lastNames. We therefore need to handle that case:

void Names::addEntry(std::string const& first,std::string const& last){
  firstNames.push_back(first);
  try{
    lastNames.push_back(last);
  } catch(...){
    firstNames.resize(lastNames.size());
    throw;
  }
}

Now, if lastNames.push_back(last) throws, then std::vector guarantees that lastNames is unchanged, so we can ensure our invariant holds again by shrinking firstNames back to the same size, and the single-threaded case is now sorted. If our Names object is only ever accessed by a single thread, then the invariant holds at all times outside the internals of a member function.

What about the concurrent case? If we call Names::addEntry from multiple threads, then everything is broken: std::vector is not safe for concurrent access from multiple threads, so we have data races and undefined behaviour. Using a ThreadSafeVector class instead which provides the operations we need but is safe for concurrent access removes these data races and the undefined behaviour, but doesn't fix the overall problem. Thread safety is not composable. In this case, the invariants are still broken during the call to Names::addEntry, so a concurrent call will see an object with broken invariants: the thread safety of the two vectors doesn't matter if the second thread can see firstNames and lastNames with different sizes.

We can fix the problem by using a mutex: the mutex lock prevents the second thread from accessing the internals of the object until the first thread has finished, so the second thread cannot see the state with a broken invariant. It also allows us to revert back to std::vector rather than ThreadSafeVector since the individual vectors are only ever accessed by one thread at a time:

class Names{
  std::mutex mutex;
  std::vector<std::string> firstNames,lastNames;

public:
  void addEntry(std::string const& first,std::string const& last){
    std::lock_guard guard(mutex);
    firstNames.push_back(first);
    try{
      lastNames.push_back(last);
    } catch(...){
      firstNames.resize(lastNames.size());
      throw;
    }
  }
};

This is one of the big reasons why lock-free data structures are so hard to write: we cannot just stick a mutex lock on the outside to ensure other threads never see broken invariants. Instead, we must ensure that the invariants hold at all times, even during the operation of member functions. For this reason, lock-free data structures are often designed so that as much as possible is done off to the side, without modifying the core data structures, and then either the entire change is committed with a single atomic operation, or care is taken to ensure that the invariants still hold after each incremental step.

End note

Invariants are important. They are vital to working with objects, and without them it is much harder to write correct code. However, you have to choose your invariants carefully: invariants that make the rest of your code easier to reason about can have a cost, so you have to be aware of the trade-offs. Like everything in programming it is up to you what trade-off you choose: simplicitly vs performance is a common choice, but it can also be a choice of which operations are fast and which are slow. Whatever you choose, there will be cases where the other choice was more appropriate.

Whatever you choose, you must be honest with yourself, and the users of your class. Don't claim something is an invariant just because it holds most of the time; it must hold all of the time. Obviously, you can make it so a precondition of a function that objects it operates on are only in certain states, and then write your program logic to ensure that the conditions are met, but don't confuse the two.

Posted by Anthony Williams
[/ cplusplus /] permanent link
Tags: , ,
Stumble It! stumbleupon logo | Submit to Reddit reddit logo | Submit to DZone dzone logo

Comment on this post

Follow me on Twitter

Struggling with Rust to figure out the right types for a function signature

Andy Balaam from Andy Balaam&#039;s Blog

I am loving writing code in Rust. So many things about the language and its ecosystem feel so right*.

* For example: ownership of objects, expressive type system, compile to native, offline API docs, immutability, high quality libraries.

One of the things I like about it is that I don’t feel like I need to use an IDE, so I can happily code in Vim with no clever plugins.

One thing an IDE might give me would be an “extract function” refactoring. In most languages I am happy to do that manually, because I can let the compile errors guide me on what my function should look like.

However, in Rust I sometimes find it’s hard to find the right signature for a function I want to extract, and I am struggling to persuade the compiler to help me.

Here is an example from my new listsync project, in listsync-client-rust.rs:

use actix_web::{middleware, App, HttpServer};
use listsync_client_rust;
// ...
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
//...
    HttpServer::new(|| {
        App::new()
            .wrap(listsync_client_rust::cookie_session::new_session())
            .wrap(middleware::Logger::default())
            .configure(listsync_client_rust::config)
    })
//...

I would like to extract the code highlighted above, the creation of an App, into a separate function, like this:

fn new_app() -> ??? {
    App::new()
        .wrap(listsync_client_rust::cookie_session::new_session())
        .wrap(middleware::Logger::default())
        .configure(listsync_client_rust::config)
}
//...
    HttpServer::new(|| {
        new_app()
    })

Simple, right? To find out what the return type of the function should be, I can just make a bad guess, and get the compiler to tell me what I did wrong. In this case, I will guess by changing the question marks above into i32, and run cargo test. I get quite a few errors, one of which is:

error[E0277]: the trait bound `i32: actix_service::IntoServiceFactory<_>` is not satisfied
  --> src/bin/listsync-client-rust.rs:27:5
   |
27 | /     HttpServer::new(|| {
28 | |         new_app()
29 | |     })
   | |______^ the trait `actix_service::IntoServiceFactory<_>` is not implemented for `i32`
   |
   = note: required by `actix_web::server::HttpServer`

So the first problem I see is that the error message I am seeing is about the later code, and there are no errors about my new function.

I obviously went a little too fast. Let’s change the HttpServer::new code back to how it was before, and only make a new function new_app. Now I get an error that should help me:

error[E0308]: mismatched types
  --> src/bin/listsync-client-rust.rs:12:5
   |
11 |   fn new_app() -> i32 {
   |                   --- expected `i32` because of return type
12 | /     App::new()
13 | |         .wrap(listsync_client_rust::cookie_session::new_session())
14 | |         .wrap(middleware::Logger::default())
15 | |         .configure(listsync_client_rust::config)
   | |________________________________________________^ expected i32, found struct `actix_web::app::App`
   |
   = note: expected type `i32`
              found type `actix_web::app::App<impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog<actix_http::body::Body>>`

So the compiler has told us what type we are returning! Let’s copy that into the type signature of the function:

use actix_service::ServiceFactory;
use actix_http::body::Body;
// ...
fn new_app() -> App<impl ServiceFactory, middleware::logger::StreamLog<Body>> {
// ...

The first error I get from the compiler is a distraction:

error[E0432]: unresolved import `actix_service`
 --> src/bin/listsync-client-rust.rs:1:5
  |
1 | use actix_service::ServiceFactory;
  |     ^^^^^^^^^^^^^ use of undeclared type or module `actix_service`

I can fix it by adding actix-service = "1.0.5" to Cargo.toml. (I found the version by looking in Cargo.lock, since this dependency was already implicitly used – I just need to make it explicit if I am going to use it directly.)

Once I do that I get the next error:

error[E0603]: module `logger` is private
  --> src/bin/listsync-client-rust.rs:13:54
   |
13 | fn new_app() -> App<impl ServiceFactory, middleware::logger::StreamLog<Body>> {
   |                                                      ^^^^^^

This leaves me a bit stuck: I can’t use StreamLog because it’s in a private module.

More importantly, it makes the point that I don’t actually want to be as specific as I am being: I don’t care what the exact type parameters for App are – I just want to return an App of some kind and have the compiler fill in the blanks. Ideally, if I change the body of new_app later, for example to add another wrap call that changes the type of App we are returning, I’d like to leave the return type the same and have it just work.

With that in mind, I took at look at the type that HttpServer::new takes in. Here is HttpServer:

impl<F, I, S, B> HttpServer<F, I, S, B> where
    F: Fn() -> I + Send + Clone + 'static,
    I: IntoServiceFactory<S>,
    S: ServiceFactory<Config = AppConfig, Request = Request>,
    S::Error: Into<Error> + 'static,
    S::InitError: Debug,
    S::Response: Into<Response<B>> + 'static,
    <S::Service as Service>::Future: 'static,
    B: MessageBody + 'static, 

and HttpServer::new looks like:

pub fn new(factory: F) -> Self

So it takes in a function which actually makes the App, and the type of that function is F, which is a Fn which returns a I + Send + Clone + 'static. From the declaration of HttpServer we can see that the type of I depends on S and B, which have quite complex types. Let’s paste the whole thing in:

use actix_http::{Error, Request, Response};
use actix_service::IntoServiceFactory;
use actix_web::body::MessageBody;
use actix_web::dev::{AppConfig, Service};
use core::fmt::Debug;
// ...
fn new_app<I, S, B>() -> I
where
    I: IntoServiceFactory<S> + Send + Clone + 'static,
    S: ServiceFactory<Config = AppConfig, Request = Request>,
    S::Error: Into<Error> + 'static,
    S::InitError: Debug,
    S::Response: Into<Response<B>> + 'static,
    <S::Service as Service>::Future: 'static,
    B: MessageBody + 'static,
{
    App::new()
        .wrap(listsync_client_rust::cookie_session::new_session())
        .wrap(middleware::Logger::default())
        .configure(listsync_client_rust::config)
}

Note that I had to modify I to include the extra requirements on the return type of F from the definition of HttpServer. (I think I did the right thing, but I’m not sure. If I just remove the + Send + Clone + 'static it seems to behave similarly.)

Now I get this error from the compiler:

error[E0308]: mismatched types
  --> src/bin/listsync-client-rust.rs:27:5
   |
17 |   fn new_app<I, S, B>() -> I
   |                            - expected `I` because of return type
...
27 | /     App::new()
28 | |         .wrap(listsync_client_rust::cookie_session::new_session())
29 | |         .wrap(middleware::Logger::default())
30 | |         .configure(listsync_client_rust::config)
   | |________________________________________________^ expected type parameter, found struct `actix_web::app::App`
   |
   = note: expected type `I`
              found type `actix_web::app::App<impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog<actix_http::body::Body>>`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

The compiler really tries to help here, suggesting I read a chapter of the Rust Book, but even after reading it I could not figure out how to do what I am trying to do.

Can anyone help me?

Wouldn’t it be amazing if there were some way the compiler could give me easier-to-understand help to figure this out?

#NoProjects everywhere

Allan Kelly from Allan Kelly Associates

iStock-173005469Medium-2020-03-3-16-10.jpg

After years of shared #NoProjects advocacy I finally for to meet Evan Leybourn exactly two years ago. Over lunch in a Melbourne cafe we talked #NoProjects and how we were both moving on. It may have been a little pre-emptive and some might call it arrogant but we felt #NoProjects was triumphant.

Two years on and projects are still with us, projects aren’t going away anytime soon but the ideas behind #NoProjects are mainstream. The leading thinkers in the software/agile/digital space generally support the thesis – certainly nobody is arguing the case for projects. Cutting edge teams don’t use projects. The language of projects remains (unfortunately) but the supremacy of the project model increasingly looks like a historical footnote.

Two years on from that lunch and it is clearer than ever that the (digital) world is moving away from projects. This was really brought home to me last week when I joined an unconference organized by the McKinsey consultancy. Nobody said “#NoProjects” but nobody was talking projects. Nobody was advocating more project managers or new project management approaches. The CTO of a bank came pretty close to saying “#NoProjects” but why bother? – not saying it meant it was accepted.

#NoProjects is in your face. #NoProjects is an invitation to start a flame war. #NoProjects is confrontation in itself. #NoProjects is very negative. #NoProjects doesn’t tell you what to do only what not to do.

Back in 2013 #NoProjects needed saying, Josh Arnold, Steve Smith and myself started Tweeting it to death – Evan came later. (I think it might have been Josh who first used the tag.)

As much as I’d like to take all the credit we were just the public face of #NoProjects. We were far from alone. Woody Zuill and Vasco Duarte started the #NoEstimates movement about the same time. While in my mind #NoProjects and #NoEstimates are different things many people see them as two sides of the same coin.

I’d first heard Mary Poppendieck talk candidly about problems with the project model over dinner at Agile on the Beach 2011. Many, many, other people were reaching the same conclusion. Once you start looking at the project model, especially in an agile environment, the problems are easy to see.

The logic against projects can be overwhelming but exposing it was a career threatening move. Even today being an open advocate for #NoProjects means there are jobs you cannot apply for. None of the original names will ever be considered for a project management job.

Look around you today: The Project Manager role is being replace by the Delivery Manager role.

SAFe is a #NoProject model.

Spotify is a #NoProject model.

Continuous Delivery is a #NoProject model.

And my own entry: Continuous Digital is certainly #NoProjects (it was written to tell you what to do instead of projects).

Sure masochists can add projects to SAFe, Spotify and CD but why? These models work well enough without projects.

Even Government departments suggest funding teams not projects.

Today, at every conference and event you will hear people say “Products over projects.” There is a realisation that products last, projects end – who wants to work in a business that plans to end?

Again, to be clear: I’m talking about the digital world, what we used to call software or IT. I don’t know about construction, transport, policing or whatever other discipline you might want to draw a parallel with. I’m sure, some projects will always exist. Somethings do end. Even I will end one day.

That the IT/software/digital world can do better than projects is now recognised. Other management models create more valuable outcomes.

One might say that #NoProjects is heading into retirement. As Josh said:

“The first rule of #NoProjects is not to talk about #NoProjects.”

So don’t wave the red flag of #NoProjects and rub peoples noses in it. For your own benefit understand where the project model goes wrong. Use that knowledge to watch for problems such as goal displacement, commitment escalation, imaginary triple lock contracts, undercutting quality, control through planning, value destruction, cost of delay ignorance, Diseconomies of Scale of course, and unlearn the project funding model.

(If you haven’t already check out my own Project Myopia or Evan’s #NoProjects book.)

Then, set a course for a better world: call it SAFe, Spotify, Products, Continuous Digital, Continuous Delivery or whatever you like. Aim to harness the power of early release, evolving design, requirements and learning. Retool your governance process and management models.

Use the carrot not the stick.


Subscribe to my blog newsletter and download Project Myopia for Free

The post #NoProjects everywhere appeared first on Allan Kelly Associates.

Visual Lint 7.0.6.316 has been released

Products, the Universe and Everything from Products, the Universe and Everything

This is a recommended maintenance update for Visual Lint 7.0. The following changes are included:

  • Fixed a bug in the parsing of Visual Studio MSBuild expressions using the [MSBuild]::GetDirectoryNameOfFileAbove() function which was preventing the system include folder path from being read correctly in Visual Studio 2019 v16.4.4.
  • Updated the values of _MSC_VER and _MSC_FULL_VER in the PC-lint Plus compiler indirect file co-rb-vs2019.lnt to support Visual Studio 2019 v16.4.4.
  • Modified the PC-lint Plus Visual Studio 2019 compiler indirect file co-rb-vs2019.lnt to suppress an instance of error 4392 (constexpr declaration follows non-constexpr declaration) in the header file $(VCToolsInstallDir_160)\include\intrin0.h.
  • Added help documentation for the fields (e.g $(index), $(SourceFile) and $(LineNo) ) which can be used in exported analysis results files.
  • Updated the helpfile to explicitly reference the PC-lint Plus analysis tool throughout.
  • Added a "PC-lint/PC-lint Plus multipass analysis" help topic.
  • Updated links within the online help as necessary.

Download Visual Lint 7.0.6.316

Rock Pi S Review

Christof Meerwald from cmeerw.org blog

Got a $20 voucher code for a review of a Rock Pi S SBC. Although the cost of the board is only $13.90, with shipping at just over $8, it's a bit over $22 in total. When the board arrived, I did actually wonder if shipping could have been made a bit more economical by not putting the tiny board into a huge box (175 mm x 115 mm x 95 mm) which had a plastic case (70 mm x 50 mm x 25 mm) with the board in it.

Anyway, two things to note about the board are that the serial console uses 1.5 Mbps (which is different to the more common 115200 bps used by other boards), and uses a USB Type C connector for power (like the Raspberry Pi 4, but unlike older SBCs).

Downloading the Ubuntu image and writing it to a SanDisk Ultra 16 GB MicroSD card seemed to work fine to get it to boot. One thing I did notice was that the SSH server keys seemed to have been written to the image and not regenerated on first boot. The other thing to be aware of is that the kernel being used is fairly old (4.4.143) with mainline kernel support only starting to appear now.

One thing that appears strange to me is that total memory is only shown as "MemTotal: 432280 kB" - I am not entirely sure what this isn't (much) closer to 512 MB.

Finally, power consumption seems to be quite good. With WiFi turned on, but otherwise completely idle, my USB tester shows the device using only around 0.2 W. Doing a "ping" and it will go up to around 0.36 W. Reading from the MicroSD card also uses around 0.4 W. Keeping all 4 cores of the CPU busy and power consumption goes up to around 0.85 W.

Source code chapter of ‘evidence-based software engineering’ reworked

Derek Jones from The Shape of Code

The Source code chapter of my evidence-based software engineering book has been reworked (draft pdf).

When writing the first version of this chapter, I was not certain whether source code was a topic warranting a chapter to itself, in an evidence-based software engineering book. Now I am certain. Source code is the primary product delivery, for a software system, and it is takes up much of the available cognitive effort.

What are the desirable characteristics that source code should have, to minimise production costs per unit of functionality? This is what an evidence-based chapter on source code is all about.

The release of this chapter completes my second pass over the material. Readers will notice the text still contains ... and ?‘s. The third pass will either delete these, or say something interesting (I suspect mostly the former, because of lack of data).

Talking of data, February has been a bumper month for data (apologies if you responded to my email asking for data, and it has not appeared in this release; a higher than average number of people have replied with data).

The plan is to spend a few months getting a beta release ready. Have the beta release run over the summer, with the book in the shops for Christmas.

I’m looking at getting a few hundred printed, for those wanting paper.

The only publisher that did not mind me making the pdf freely available was MIT Press. Unfortunately one of the reviewers was foaming at the mouth about the things I had to say about software engineering researcher (it did not help that I had written a blog post containing a less than glowing commentary on academic researchers, the week of the review {mid-2017}); the second reviewer was mildly against, and the third recommended it.

If any readers knows the editors at MIT Press, do suggest they have another look at the book. I would rather a real publisher make paper available.

Next, getting the ‘statistics for software engineers’ second half of the book ready for a beta release.

All about Agile on the Beach

Allan Kelly from Allan Kelly Associates

This week has been a bit of a feast about Agile On the Beach. So here is a list of this weeks writing and some previous posts.

And some older entries that explain us:

This possibly shows that AOTB takes up far more of my life than it should! 🙂

The post All about Agile on the Beach appeared first on Allan Kelly Associates.

Mimas is OpenSource

Allan Kelly from Allan Kelly Associates

256px-Mimas_Cassini-2020-02-24-11-36.jpg

That picture above is Mimas, one of the moons of Saturn – yes, I’m still a bit of a “geek”. Mimas is the name I chose for the Agile on the Beach submission system I started developing about four years ago.

I am a bit surprised that Mimas has grown to over 10,000 lines – mainly Python and HTML, a very small amount of JavaScript and a super tiny bit of CSS. O, and 239 unit tests which now take about 8 second to run. (Like so many other systems the front end isn’t covered by automated tests. This hasn’t been a big problem, the tests I have giv great confidence.)

I’m not completely surprised by the fact that four years later we are still using it.

The code has lots of failings – doesn’t every system? – but I’m immensely proud of it as more than I’m embarrassed by it. I’m particularly proud of the way the system held up to 300 submissions and 55 reviewers this year. This was the most use it has ever had.

As of last month the system is also OpenSource – MIT License. You can browse the code – and tell me about all my failings: Mimas on GitHub.

The system itself runs on the Google App Engine here. Anyone can log in and create their own conference. Although this being the post-review period, and me having some time, I’m taking the opportunity to make some improvements. If it is down it won’t be down for long. Unfortunately Google are forcing a database upgrade (NDB to Cloud NDB) on a move from Python 2.7 to 3.x (which I should have done a while back.)

Once upon a time I thought that other people might like to use the system for their conferences but nobody ever has. The offer is still there.

I’d love it if anyone else wants to help with development on the system – but I’m realistic to know this is unlikely. (If you want to help there are a lot of places in the UI where a little JS or CSS could improve things. In the backend I know a few places that could do with changes too.)

For the record this is what Mimas does:

  • Conference organisers create a conference and accept submissions.
  • Speaker details are retained for future conferences as are talk details.
  • Submissions are accepted into tracks, they can have co-speakers, formats and duration are configurable.
  • Two rounds of reviewing are supported, these are configurable. AOTB uses scoring for round 1 and ranking for round 2.
  • Reviewers can volunteer and be assigned (random) submissions to review.
  • Conference permissions allow for different users to have different capabilities.
  • E-mail acknowledgements, acceptances and declines are handled via SendGrid – you can also define custom messages via templates.
  • Half a dozen or more types of report plus export to Excel capability.
  • OAuth login currently supporting e-mail, Google and Facebook via Firebase.
  • Share reviewer feedback and comments with submitters.
  • Some conference branding.
  • (There is an open speakers directory and tagging mechanism, I should either improve this or scrap it.)

I’m currently building out a scheduler so the accepted submissions can be organised in the system too. Its half done.

There are bunch of ways I could develop it further: it could support paper review rather than conference review, it could support shepherding, it could be used for reoccurring events, and a whole lot more.

Finally, it is surprisingly cheap to run on the Google App Engine.

Anyway, its OpenSource, its live, let me know what you think.

The post Mimas is OpenSource appeared first on Allan Kelly Associates.