We are developing a multi-user web-based application, where the users can join a "room" and a complicated handshake has to be set up between them, to be able to use a library on each frontend side. This session/room logic and the handshake that has to be set up goes through a backend server with WebSocket communication. We are using a WebSocket based RPC library. I am a junior backend dev and was tasked with developing this core backend logic, handling connections and disconnections and supporting the appropriate handshakes.
I developed this backend, but by developing the backend, of course I basically also developed the whole communication protocol: how various method calls should come in order to build up the handshake. I paid a lot of attention to avoiding race conditions by messages coming in wrong order. I even paid a lot of attention to how the frontend library we are using would affect the order of messages. I made some kind of documentation, and explained our protocol to the frontend guy, to let him start work on the frontend part of this communication.
However, he did not really understand it, or even when he claimed to understand my explanations, he could not reason out the answer himself to his questions (i.e. when various WebSocket listeners have to be set up on the frontend, how to set up various data structures based on data from the backend, what the actual arguments of a method call to the server need to be), and in the end I ended up basically instructing him down to the detail how to write his frontend code for this. Part of the reason seems to be that while I have a BSc in Computer Science, so I think about this task in terms of concurrent programming, race conditions, interleaving threads, mutual exclusion, etc. he is a self-taught or bootcamp graduate, so he does not have these mental concepts readily available.
However, this can't go on and he has to be able to reason about our protocol, and he should be able to perform any future changes on the frontend without bringing back concurrency issues that I worked hard to avoid. Additionally, we will probably interface with another team at some point, who will develop a client for another platform. My backend tech lead is going to represent us in these meetings, and he is not completely up to date on the details of my work. I am thinking, how could I help the frontend guy, my tech lead and the other team with more documentation?
Currently, we have the following documentation:
- A UML sequence diagram detailing the basic happy path of communication between three clients and the server. The diagram shows method names and parameter names, differentiates between method calls and return values, is color coded, is split into named interaction segments, and has a key.
- A remote method reference for both the server and the client. This currently includes parameter types, and an informal description of the use case of the method. It looks something like this for server methods:
JoinRoom(roomId: string)
You as a user should invoke this method to join the room with the given id, once it is started. The return value is a User[] detailing data of users in the room.
KickOut(roomId: string, userId: guid)
You as an admin should invoke this method to kick out users from the room. The return value is a ResultCode.
SendFoo(foo: object, recipientId: string, senderId: string)
You as a user who joined the room, should invoke this method to start the FooBar handshake with the recipient. The return value is a ResultCode.
SendBar(bar: object, recipientId, string, senderId: string)
You as a user who got the Foo message, should invoke this method to finish the FooBar handshake. The return value is a ResultCode.
SendBaz(baz: object)
Sends the Baz info for all the participants in the room.
And looks something like this for client methods:
UserJoined(user:User)
This method is invoked when a new user joins the room.
UserLeft(userId: string)
This method is invoked when a user leaves the room.
Foo(foo: object, recipientId: string, senderId: string)
This method is invoked when a user starts the FooBar handshake with you. You should answer with the SendBar call.
How can I extend this documentation to be more helpful to everyone else? I realize some things are not covered that lead me to some ideas for extending the material:
- Since the sequence diagram is a single timeline, complicated error paths are not covered there. Maybe I could extend the diagram to be an interaction overview diagram which would then reference smaller snippets of the sequence diagram. PRO: this would show error paths CON: I think interaction overview diagram could be even more overwhelming than a simple sequence diagram
- The method reference doesn't actually help much with the race condition aspect: I could spell out preconditions of methods more exactly:
SendBaz(baz: object)
Prerequisites: received Foo, sent SendBar
Sends the Baz info for all the participants in the room.
- More info on how to interact with the FooLib library we are using on the frontend and how to store the data (even though it is not really my area of responsibility):
SendBaz(baz: object)
Prerequisites: received Foo, called SetFoo in FooLib, saved the Foo to foosByUserId dict, sent SendBar
Frontend implementation notes: this should be called from the onBar event handler of the FooLib. The handler should be set before calling SetFoo
Sends the Baz info for all the participants in the room.
- Adding a short description of these concurrency issues specific to the app to the intro of the documentation, explaining the issues without assuming everyone knows the technical terms
Which of these ideas would be the most helpful?