Skip to content

2021🔗

Keyboard Remapping in Goland

This is a continuation of my evaluation using Goland.

I tend to be a VSCode user primarily, but am working on adopting Goland incrementally to leverage some of it's features that work a bit more consistently (like refactoring).

Losing keyboard shortcuts is painful.

VSCode lets you assign keyboard shortcuts in command pallette by clicking on the gear icon in the command you hover over. For a while I couldn't find this in Goland. I see now I can assign any action defined from the quick open menu by pressing Cmd+..

This should expedite keyboard shortcut customization to align to my muscle memory a bit easier.

Go R1 Day 80

progress

Built a Mage task to handle creation of Azure DevOps pull requests. Since the tooling out there is primarily around Github, this was useful to help standardize PR creation with autocomplete, conventional commit naming, and other properties that typically require manual changes. I found a great little TUI components library that simplified using Bubbletea components: Promptkit.

In addition, noticed some new linting help from golangci-lint for varnamelen.

This was useful as it analyzes the brevity of variable names and if the variable name is too short, say 1-3 characters, but the usage extends 20 lines away, it will flag it. This is good as short variable names are designed for local context, while longer descriptive names provide better readability further away in the code.

Practical Go: Real world advice for writing maintainable Go programs - Identifier Length

Golangci-lint tool includes this linter: Varnamelen

Go R1 Day 79

progress

Felt like revisiting Exercism since the great updates and wanting a break from the more deep concept dives on mechnical sympathy and optimization in the Ultimate Go course (which is stellar).

Completed the following to continue on syllabus. It's taking me back to basics, but stuff I can knock out pretty quick.

From this I determined that I'm great at writing chained boolean statements.

if goodAtBool && coderIsMe {
    fmt.Println("I'm a good coder")
} else if goodAtBool && !coderIsMe {
    fmt.Println("I'm a good coder")
} else if !goodAtBool && coderIsMe {
    fmt.Println("I'm a bad coder")
} else {
    fmt.Println("I'm a bad coder")
}

Thank you GitHub Copilot for the codeblock above. I claim no responsibility to the results of what AI generated madness was created.

Go R1 Day 78

progress

Ultimate Go: 2.3.4 - Pointers-Part 4 (Stack Growth)

Scenario:

  • 2k stack for each goroutine.
  • 50,000 goroutines.
  • Eventually, you'll want to make the function call and you'll want to grow the stack if the current stack limit is hit.
  • We'll want to use contigous stacks.
  • The new stack will be a new contigous allocated block of memory.
  • The stack growth requires all the prior values to be moved over to the new doubled stack.

A goroutine can only share values from the heap. This prevents the issues occuring from shared values in different stacks.

Ultimate Go: 2.3.5 - Pointers-Part 5 (GC)

Mark and sweep collector.

We don't need to worry about the implementation.

However, this topic is useful to ensure we write code that is "sympathetic" to the GC.

At this point, I opted to come back to GC details and focus on some testing and package design principles.

Go R1 Day 77

progress

More Fun With Golangci-lint

Been doing a ton the last month with golangci-lint tooling. I think this has been one of the most educational tools for learning Go I've come across. It has forced me to evaluate why issues are flagged, if they are opinionated preferences or best practices.

For example, wsl ensures that statements are not cuddled. This follows Dave Cheney's writing about having an empty line seperate phrases of thought.

It's a bit annoying to implement as a linter though, and can't be applied programaticaly so I'm not using that.

Linting - Shadowing Package Names

Another one that I caught from Goland linting today, that golangci-lint didn't seem to catch, was the shadowing of a package name.

In this scenario I found code where:

package taco


func main() {
 taco := taco.Method()
}

While this is legal, it's a confusing practice, and thereafter prohibits the usage of the taco package as it's been overshadowed by the variable.

To me this is a clear violation of Go's preference for "no magic" and readability.

In this scenario, the fix is simple. Change the variable name used or alias the package (my preference).

package (
  pkgtaco "taco"
)


func main() {
 taco := pkgtaco.Method()
}

Linting - Handling Errors

Also did some investigation on errcheck and flagging of handling file close and response body closing for http. This is one of those areas that linters flag and it's a "it depends" and not very consistent.

Basically the gist is ignore, except if file writing is occuring then it's probably needing an explicit handle.

Go R1 Day 76

progress

Lots of more technical detail on Memory & Data Semantics Section.

I value this as my background in databases means that things like Stack/Heap weren't part of my education as much. This has been useful as there are new concepts, paradigms, and ways of thinking and it's providing a lot more foundational concepts for me to better understanding the system and compiler behavior in Go.

Ultimate Go: Pointers-Part-3: Escape Analysis

Notes
  • Stacks are self-cleaning
  • Compiler knows what goes on stack and what goes on heap at compile time. Knowing where the value is constructed is part of the static analysis the compiler performs.
  • Bill said this is a really powerful feature that will probably impact future languages.
  • When you see a pointer in the return value, this could be phrased "shared up the callstack".
  • This means that the construction inside the function would be on the heap not the stack.
  • If you are trying to access items further up on the stack, it requires pointers.
  • Bill said what's cool about this is the ability to access an item on the heap via this pointer, as if we were working with pointers to a stack value. It abstracts the machine level details away so we benefit from easy out of frame access to values with pointers.
  • We don't have to worry about where the location of the value being constructed is. Due to escape analysis, the compiler will determine the best place, but the reality is some items have to go to the heap, and the compiler is smart enough to help determine this.
  • Why does this matter? Bill says that if performance matters, understanding the concepts matter because garbage collection and other latencies are impacted by these concepts.
  • Can view escape analysis from compiler with: go build -gcflags -m=2.
    • I tested this on one of my projects, and found that function complexity can prevent inlining, lots of escape to heap references and more.
    • While not needed for basic build automation type helpers, I could see the value in scalable design for Go in examining this in more details. It's almost like using SQL Server execution plans to optimize for higher performance.
Takeaways
Don't hide the cost

If we are doing construction to a variable, we use value construction. Avoid pointer semantic construction if not in the return.

Example:

// clear visible cost of the allocation by value construction and passing of pointer back up the call stack
func createSomething() *something {
  u := something{
    name: "example",
  }
  return &u // <--- This makes clear the cost and allocation back up the callstack.
}
// cost is obscured by construction being a pointer
// and returning a value that is not clear to reader if value or pointer
func createSomething()*something {
  u := &something{
    name: "example",
  }
  return u // <--- Not good. Hides the cost, and require reading function further to find that this is a pointer.
}

Making cost obvious and visible is a big priority for readable maintainable code with a team.

Go R1 Day 74

progress

  • Worked with DynamoDB schema (NoSQL Document database).
  • Invoked local lambda function using Docker and also remove invocation with serverless invoke. This took a bit of work to figure out as the parameters weren't quite clear. Upon using --path for the json template I got from AWS Lambda console, I was able to to get to invoke, and stream the logs with --log.
  • More mage magic with promptui and other features, so I can now test a full tear down, build, publish, and invoke a test selection by running: mage sls:destroy build sls:deploy sls:test remote.

Go R1 Day 73

progress

  • I'm more on day 90+ but haven't made the time to log it.
  • Been working a ton with Mage and have some really cool work I'll be writing up soon, including using for health checks on projects, setup of core tooling, encoding credentials, and more. 💯
  • Also been working heavily on Go mono-repo module structure and some serverless based architecture tests that will make a fun write-up. I'm thinking of a demo of "Disappointment as a Service" with a lambda driven api for tracking disappointing events and returning something to help bring reality-checks to us all. 😆

  • I used a snippet from a blog today that allowed buffering stdout from go commands to capture and avoid streaming each line by wrapping up in a pterm spinner component. Pretty neat!

Go R1 Day 72

progress

  • Learned more on "mechanical sympathy" related to Go and memory management.
  • How the Go compiler works related to frames with memory allocation.
  • Built Mage tasks for licensing and validating problematic licenses aren't included in a project.