Learning how to write "good" go code (in some definition of good) sometimes mean you need to unlearn old habits. This is especially true when it comes to asynchronous code if you have a C# background.
When writing C# code that needs to do something that might take some time it is good practice to provide an asynchronous API for others to use. I would say that you should provide only an asynchronous API since anything asynchronous can easily be converted into something synchronous while the opposite comes with a cost - typically blocking a thread somewhere. Hence you end up with methods like ReadAsync and DoStuffAsync.
In go however it is good practice to have a clear life-time of any go routine started. As go routines are so cheap to start and run, even when all of them are blocking I would say that the opposite from C# is encouraged. That is, as long as you provide a synchronous API anybody can start a go routine themselves if they want it. That way the calling code have better context on how long a certain go routine is alive.
To be fair properly written C# code will also clearly show how long an asynchronous method is active for as it should return a Task object that is completed once the asynchronous operation is completed. The equivalent in go would be to always return a channel to indicate completion. This is however not the recommendation as your code would be cluttered with channel receivers.
A better way to make sense of this (as a C# developer) is to think about the fact that the async/await keywords in C# help you write asynchronous code that looks synchronous and hence is easier to understand. Yet you can easily shoot yourself in the foot and mess it up.
Go throws this around; just write synchronous code and if you ever need something asynchronous - just do it synchronously in a go routine. You can still shoot yourself in the foot but in my experience it is harder in go especially as you only use go routines when you absolutely need them rather than async/await all the time as in C#.