From bee1ba13837cfbd37dc1a4e2efdb092291f46efb Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Fri, 14 Jun 2019 16:23:50 -0400 Subject: [PATCH] program structure: small changes, but I think I need to refactor --- .../program-structure-and-composability.md | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/_drafts/program-structure-and-composability.md b/_drafts/program-structure-and-composability.md index ce14f40..ae3486a 100644 --- a/_drafts/program-structure-and-composability.md +++ b/_drafts/program-structure-and-composability.md @@ -110,7 +110,7 @@ in the directory structure. **What the directory structure reflects are the different _kinds_ of components available to use, but it does not reflect how a program will use those components.** -### Global State vs. Compartmentalization +### Global State vs Compartmentalization The directory-centric approach to structure often leads to the use of global singletons to manage access to external resources like RPC servers and @@ -546,6 +546,8 @@ func main() { ### Full example +TODO + ## Part 3: Annotations, Logging, and Errors Let's shift gears away from the component structure for a bit, and talk about a @@ -581,7 +583,7 @@ func (app *App) GetUsername(userID int) (string, error) { } ``` -In that example, when redis returns an error the error is extended to include +In that example, when redis returns an error, the error is extended to include contextual information about what was attempting to be done (`could not get username`) and the userID involved. In newer versions of Go, and indeed in many other programming languages, the error will also include information about where @@ -596,7 +598,7 @@ performing a redis call, what good is it to see the log entry `redis command had an error: took too long` without also knowing which command is involved, and which endpoint is calling it? Very little. -So many programs of this nature end up looking like this: +Many programs end up looking like this: ```go func (app *App) httpEndpointA(rw http.ResponseWriter, r *http.Request) { @@ -618,7 +620,7 @@ func (app *App) httpEndpointB(rw http.ResponseWriter, r *http.Request) { Obviously logging is taking up the majority of the code-space in those examples, and that doesn't even include potentially pertinent information such as IP -address. +address, or log entries for non-error events. Another aspect of the logging/error dichotemy is that they are often dealing in essentially the same data. This makes sense, as both are really dealing with the @@ -626,6 +628,7 @@ same thing: capturing context for the purpose of later debugging. So rather than formatting strings by hand for each use-case, let's instead use our friend, `context.Context`, to carry the data for us. + ### Annotations I will here introduce the idea of "annotations", which are essentially key/value @@ -645,3 +648,13 @@ func Annotate(ctx context.Context, keyvals ...interface{}) context.Context // Annotate. func Annotations(ctx context.Context) map[interface{}]interface{} ``` + +### Aside: Structural vs Runtime Contexts + +It may seem strange that we're about to use Contexts for a use-case that's +completely different than the one discussed in Part 1, and I've been asked +before if perhaps that doesn't indicate the two should be separated into +separate entities: a structural context type which behaves as shown in Part 1, +and a runtime context type whose behavior we've just looked at. + +I think this is a compelling idea...