Distributed Programming: Theory and Practice