|
February 2017 - Posts
-
For the last several years, I've made more and more of my living via entrepreneurial
pursuits. I started my career as a software developer and then worked my way
along that career path before leaving fulltime employment to do my own thing.
These days, I consult, but I also make training content, write books, and offer productized
services.
When you start to sell things yourself, you come to appreciate the value of marketing.
As a techie, this feels a little weird to say, but here we are. When you have
something of value to offer, marketing helps you make interested parties aware of
your offer. I think you'd like this and find it worth your money, if you
gave it a shot.
In pursuit of marketing, you can use all manner of techniques. But today, I'll
focus on a subtle one that involves generating a good reputation with those who do
buy your products. I want to talk about making good documentation.
The Marketing Importance of Documentation
This probably seems an odd choice for a marketing discussion. After all, most
of us think of marketing as what we do before a purchase to convince customers
to make that purchase. But repeat business from customer loyalty counts for
a lot. Your loyal customers provide recurring revenue and, if they love their
experience, they may evangelize for your brand.
Providing really great documentation makes an incredible difference for your product.
I say this because it can mean the difference between frustration and quick, easy
wins for your user base. And, from a marketing perspective, which do you think
makes them more likely to evangelize? Put yourself in their shoes. Would
you recommend something hard to figure out?
For a product with software developers as an end user, software documentation can
really go a long way. And with something like GhostDoc's "build help documentation"
feature, you can notch this victory quite easily. But the fact that you can
generate that documentation isn't what I want to talk about today, specifically.
Instead, I want to talk about going the extra mile by customizing it.
Introducing "Conceptual Content"
You can easily generate documentation for your API with the click of a button.
But you can also do a lot more.
GhostDoc Enterprise features something called "Conceptual Content." Basically,
it allows you to customize and add on to what the engine generates using your code
and XML doc comments. This comes in handy in ways limited only by your imagination,
but here are some ideas.
-
A welcome page.
-
A support page.
-
A "what's new" page.
-
Including a EULA/license.
-
Custom branding.
You probably get the idea. If you already look to provide documentation for
your users, you no doubt have some good additional thoughts for what they might value.
Today, I'm going to show you the simplest way to get going with conceptual content
so that you can execute on these ideas of yours.
How It Works at a High Level
For GhostDoc to work its documentation-generating magic, it creates a file in your
solution directory named after your solution. For instance, with my
ChessTDD solution, it generates a file called "ChessTDD.sln.GhostDoc.xml."
If you crack open this file, you will see settings mirroring the ones you select in
Visual Studio when using GhostDoc's "Build Help Documentation."
To get this going, we face the task of basically telling this file about custom content
that we will create. First, close out of Visual Studio, and let's get to work
hacking at this thing a bit. We're going to add a simple, text-based welcome
page to the standard help documentation. To do this, we need the following things.
-
Modifications to the GhostDoc XML file.
-
The addition of a "content" file describing our custom content,
-
Addition of a "content" folder containing the AML files that make up the actual, custom
pages.
Let's get started.
The Nuts and Bolts
First, open up the main GhostDoc XML file and look for the "ConceptualContent" section.
It looks like this.
<ConceptualContent> <ContentLayout /> <MediaContent /> </ConceptualContent>
In essence, this says, "no conceptual content here." We need to change that.
So, replace the empty ContentLayout entry with this (substituting the name of your
solution for "ChessTDD" if you want to follow along with your own instead of my ChessTDD
code.)
<ContentLayout file="ChessTDD.content" folder="Content\" />
Next up, you need to create the file you just told it about, ChessTDD.content.
This file goes in the same directory as your solution and looks like this.
<?xml version="1.0"
encoding="utf-8"?> <Topics> <Topic id="4684cc2f-3179-4871-b7a4-ad69f0f260a3" visible="True" isDefault="true" title="Welcome"> <HelpKeywords> <HelpKeyword index="K" term="Welcome" /> </HelpKeywords> </Topic> </Topics>
For the ID, I simply generated a GUI using
this site. This ID simply needs to be unique, and to match the next file
that we'll create. Next up, create the folder you told ContentLayout about called,
"Content." Then add the file Welcome.aml to that folder, with the following
text.
<?xml version="1.0"
encoding="utf-8"?> <topic id="4684cc2f-3179-4871-b7a4-ad69f0f260a3" revisionNumber="1"> <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink"> <introduction> <para>Welcome
to our help section!</para> </introduction> </developerConceptualDocument> </topic>
Notice that we use the same GUID here as in the content file. We do this in
order to link the two.
Let's Give it a Whirl
With your marked up Ghost Doc XML file, the new content file, and the new Content
folder and welcome AML file, you can now re-launch Visual Studio. Open the solution
and navigate through GhostDoc to generate the help documentation CHM file.
There you have it. Now you can quickly add a page to the automatically generated
help documentation.
Keep in mind that I did the absolute, dead simplest possible thing I could do for
demonstration purposes. You can do much more. For example:
-
Adding images/media to the pages.
-
Have cross-links in there for reference.
-
Add snippets and examples.
-
Build lists and tables.
As I said earlier, you'll no doubt think of all manner of things to please your user
base with this documentation. I suggest getting in there, making it your own,
and leaving a nice, personal touch on things for them. When it comes to providing
a good user experience, a little can go a long way.
Related resources
Learn
how GhostDoc can help to simplify your XML Comments, produce and maintain quality
help documentation.
About the Author
Erik Dietrich
I'm a passionate software developer and active blogger. Read about me at my
site. View
all posts by Erik Dietrich
|
-
In what has become a
series of posts, I have been explaining some CodeIt.Right rules in depth.
As with the last post in the series, I'll start off by citing two rules that I, personally,
follow when it comes to static code analysis.
-
Never implement a suggested fix without knowing what makes it a fix.
-
Never ignore a suggested fix without understanding what makes it a fix.
It may seem as though I'm playing rhetorical games here. After all, I could
simply say, "learn the reasoning behind all suggested fixes." But I want to
underscore the decision you face when confronted with static analysis feedback.
In all cases, you must actively choose to ignore the feedback or address it.
And for both options, you need to understand the logic behind the suggestion.
In that spirit, I'm going to offer up explanations for three more CodeIt.Right rules
today.
Use Constants Where Appropriate
First up, let's consider the admonition to "use constants where appropriate."
Consider this code that I lifted from a
Github project I worked on once.
I received this warning on the first two lines of code for this class. Specifically,
CodeIt.Right objects to my usage of static readonly string . If I let
CodeIt.Right fix the issue for me, I wind up with the following code.
Now, CodeIt.Right seems happy. So, what gives? Why does this matter?
I'll offer you the
release notes of the version where CodeIt.Right introduced this rule. If
you look at the parenthetical next to the rule, you will see "performance."
This preference has something to do with code performance. So, let's get specific.
When you declare a variable using const or static readonly ,
think in terms of magic values and their elimination. For instance, imagine
my UserAgentKey value. Why do you think I declare that the way
I did? I did it to name that string, rather than using it inline as a "magic"
string.
As a maintenance programmer, how frustrating do you find stumbling across lines of
code like, "if(x == 299)"? "What is 299, and why do we care?!"
So you introduce a variable (or, preferably, a constant) to document your intent.
In the made-up hypothetical, you might then have "if(x == MaximumCountBeforeRetry)".
Now you can easily understand what the value means.
Either way of declaring this (constant or static, readonly field) serves the replacement
purpose. In both cases, I replace a magic value with a more readable, named
one. But in the case of static readonly , I replace it with a variable,
and in the case of const , I replace it with, well, a const.
From a performance perspective, this matters. You can think of a declaration
of const as simply hard-coding a value, but without the magic. So, when I switch
to const, in my declaration, the compiler replaces every version of UserAgentKey with
the string literal "user-agent". After compilation, you can't tell whether I
used a const or just hard-coded it everywhere.
But with a static readonly declaration, it remains a variable, even when
you use it like a constant. It thus incurs the relative overhead penalty of
performing a variable lookup at runtime. For this reason, CodeIt.Right steers
you toward considering making this a constant.
Parameter Names Should Match Base Declaration
For the next rule, let's return to the Github scraper project from the last example.
I'll show you two snippets of code. The first comes from an interface definition
and the second from a class implementing that interface. Pay specific attention
to the method, GetRepoSearchResults .
If you take a look at the parameter names, it probably won't surprise you to see that
they do not match. Therein lies the problem that CodeIt.Right has with my code.
It wants the implementing class to match the interface definition (i.e. the "base").
But why?
In this case, we have a fairly simple answer. Having different names for the
conceptually same method creates confusion.
Specifically, maintainers will struggle to understand whether you meant to override
or overload the method. In our mind's eyes, identical method signatures signals
polymorphic approaches, while same name, different parameters signals overload.
In a sense, changing the name of a variable fakes maintenance programmers out.
Do Not Declare Externally Visible Instance Fields
I don't believe we need a screenshot for this one. Consider the following trivial
code snippet.
public class SomeClass
{ public string _someVariable;
}
This warning says, "don't do that." More specifically, don't declare an instance
field with external (to the type) visibility. The question is, "why not?"
If you check out the Microsoft guidance on the subject, they explain that, the "use
of a field should be as an implementation detail." In other words, they contend
that you violate encapsulation by exposing fields. Instead, they say, you should
expose this via a property (which simply offers syntactic sugar over a method).
Instead of continuing with abstract concepts, I'll offer a concrete example.
Imagine that you want to model a family and you declare an integer field called _numberOfChildren .
That works fine initially, but eventually you encounter the conceptually weird edge
case where someone tries to define a family with -1 children. With an integer
field, you can technically do this, but you want to prevent that from happening.
With clients of your class directly accessing and setting this field, you wind up
having to go install this guard logic literally everywhere your clients interact with
the field. But had you hidden the field behind a property, you could simply
add logic to the property setter wherein you throw an exception on an attempt to set
a negative value.
This rule attempts to help you future-proof your code and follow good OO practice.
Until Next Time
Somewhat by coincidence, this post focused heavily on the C# flavor of object-oriented
programming. We looked at constants versus field access, but then focused on
polymorphism and encapsulation.
I mention this because I find it interesting to see where static analyzers take you.
Follow along for the rest of the series and, hopefully, you'll learn various useful
nuggets about the language you use.
Learn
more how CodeIt.Right can help you automate code reviews and improve your code quality.
About the Author
Erik Dietrich
I'm a passionate software developer and active blogger. Read about me at my
site. View
all posts by Erik Dietrich
|
-
Editorial note: most of the GhostDoc bloopers below are a history, but they provide
for a good laugh. We encourage you to share the GhostDoc goofs you have encountered
– they are great for entertainment as well as give us a chance to improve the product.
Some years ago, I was doing work for some client or another. Honestly, I have
no recollections of specifics with the exception of a preference for exhaustive commenting.
Every class, every method, every property, and every field.
Of course, I didn't learn this at first. I didn't even learn it in a reasonable
time frame. Instead, I learned it close to handover time. And so things
got a little desperate.
Enter GhostDoc, My Salvation
Now, depending on your perspective, you might scold me for not diligently commenting
all along. I will offer the explanation that the code had no public component
and no intended APIs or extensions. It also required no "why" types of explanations;
this was simple stuff.
The client cited policy. "We comment everything, and we're taking over this
code, so we want you to do the same." Okie Dokie.
Now, I knew that in a world of code generation and T4 templates, someone must have
invented a tool that would generate some sort of comments or another. At the
time, a quick Google search brought me to a saving grace: the free tool GhostDoc.
While it did not allow me to carpet bomb my code with comments in a single click (and
understandably so), it did allow me to do it for entire files at a time. Good
enough. I paid my non-commenting penance by spending an hour or so commenting
this way.
And do you know what? It generated pretty respectable comments. I recall
feeling impressed because I expected empty template comments. Instead, GhostDoc
figured out how to string some verbs and nouns together.
But It's Not All Roses
Let me get specific about capabilities and limitations. Had I tried to generate
documentation for my
Chess TDD codebases's MovePiece method, GhostDoc would have treated me to this
comment.
/// <summary> /// Moves
the piece. /// </summary> /// <param
name="origin">The origin.</param> /// <param
name="destination">The destination.</param> public void MovePiece(BoardCoordinate
origin, BoardCoordinate destination) { VerifyCoordinatesOrThrow(origin, destination); var pieceToMove = GetPiece(origin);
AddPiece(pieceToMove, destination); RemovePiece(origin); pieceToMove.HasMoved = true;
ReconcileEnPassant(origin, destination, pieceToMove); }
See what I mean? I did a pretty good job with clear naming in this method, and
GhostDoc rewarded me with a method header that stands on its own. MovePiece()
moves the piece. Specifically, it moves the origin to the destination.
Not too shabby!
But what about less intuitive naming structures? Well, sometimes, things can
get weird.
Do you know there was a Visual Studio addon that generated comments from method names?
I once inherited a project full of these. pic.twitter.com/fuFjZjz4LJ
— Dan Abramov (@dan_abramov) December
15, 2016
Yes, that's right. The ubiquitous "main" method for a C# application "mains
the args."
What Else?
Okay, that ought to furnish a chuckle. But now that you see the issue, you can
probably start to imagine all manner of ways to flummox the poor tool. (As an
aside, you can probably also understand why they want to force you to pay more attention
to the generated comments than just saying "generate comments for the whole solution.")
What do you think used to happen back then for a no-op method that you named "DoNothing?"
You'd get "Does the Nothing." Good work on conjugation, but still missing the
intent. How about an event handler called "AfterInsert?" That's right.
"Afters the Insert." And "TaskComplete?" "Tasks the completed."
Now, here's a tougher one: "OnClose." You probably weren't expecting "Oncloses
the instance."
Of course, other idiomatic programming concerns also befuddled the tool in different
ways.
-
ToModel() --> "Toes the model."
-
ToTitleCase() --> "Toes the title case."
-
ToComment() -> "Automatics the comment"
-
ForgetPassword() --> "Forgets password"
-
SignOut() --> "Signs the out"
Or how about
Historically, you could make GhostDoc generate some pretty hilarious comments.
This means that if you simply generated the comments and paid no further attention,
you might look pretty absurd to someone auditing commit history. Fortunately,
my oversight of the generated comments saved me from that fate.
Laugh at the Tool?
So should you laugh at GhostDoc? Well, sure. These comments are funny,
and everyone ought to have a sense of humor.
But on a deeper level, no, not really. Natural language processing presents
a difficult problem. Imagine yourself writing code to process class, method,
and variable names. At first, you might thing it easy. But then you'd
hit things like "ToString()" and "TaskComplete()." As a human and programmer,
you understand these nuances. But how do you make your code do the same?
Oh, sure, you can hard-code some overrides for unique situations. But that will
quickly become cumbersome. As you can see, you have a hard problem on your hands.
What You Can Do
What should you do, then, as a user of GhostDoc? Well, first and foremost, don't
just mindlessly generate comments. At the bare minimum, do what I did.
Mindlessly generate comments, but also look to make sure that they make sense, correcting
any that don't.
But, beyond that, use GhostDoc as a starting point for meaningful communication.
Generate XML doc comments only when doing so to communicate with someone, either via
code comments or IntelliSense/help documentation. And when you do that, understand
that GhostDoc simply offers you an intelligent starting point. The tool does
not purport to replace you as a programmer or a communicator of your intent.
It just gets you started.
Improving the Situation
Of course, GhostDoc has improved over the years. For instance, if I write a
ToString() method and generate, it no longer offers me a "toes the string."
Instead, it does this.
/// <summary> /// Returns
a <see cref="System.String" /> that
represents this instance. /// </summary> /// <returns>A <see
cref="System.String" /> that represents this
instance.</returns> public string ToString()
{ return "I'm
a board.";
}
Now, GhostDoc actually does something pretty sophisticated. It figures out that
ToString() has a special place in the language and adapts accordingly, explaining
that place with a marked-up comment.
Similarly, if you try to reproduce some of the shenanigans I detailed above, you will
receive less obtuse results. The people working on the GhostDoc codebase are
constantly seeking to improve the quality of the generated comments, even if they
only serve as a starting point. But they could always use some help, so here
is what I propose.
Luckily, there is a Summary
Override feature in GhostDoc, and you should use it to improve the future generated
comments once you find a new blooper.
Write in or comment with funny or weird things that you can get GhostDoc to produce
when you generate documentation. Seriously. It'll give you, me, the readership,
and the tool authors a good laugh. But it will also bring deficiencies to their
attention, allowing them to address, correct, and improve. So go ahead.
What are your favorite GhostDoc bloopers?
Learn
more about how GhostDoc can help simplify your XML Comments, produce and maintain
quality help documentation.
About the Author
Erik Dietrich
I'm a passionate software developer and active blogger. Read about me at my
site. View
all posts by Erik Dietrich
|
-
If you haven't lived in a techie cave the last 10 years, you've probably noticed JavaScript's
rise to prominence. Actually, forget prominence. JavaScript has risen
to command consideration as today's lingua
franca of modern software development.
I find it sort of surreal to contemplate that, given my own backstory. Years
(okay, almost 2 decades) ago, I cut my teeth with C and C++. From there, I branched
out to Java, C#, Visual Basic, PHP, and some others I'm probably forgetting.
Generally speaking, I came of age during the heyday of object oriented programming.
Oh, sure I had awareness of other paradigms. In college, I had dabbled with
(at the time) the esoteric concept of functional programming. And I supplemented
"real" programming work with scripts as needed to get stuff done. But object-oriented
languages gave us the real engine that drove serious work.
JavaScript fell into the "scripting" category for me, when I first encountered it,
probably around 2001 or 2002. It and something called VBScript competed
for the crown of "how to do weird stuff in the browser, half-baked hacky languages."
JavaScript one that battle and cemented itself in my mind as "the thing to do when
you want an alert box in the browser."
Even as it has risen to prominence and inspired a generation of developers, I suppose
I've never really shed my original baggage with it. While I conceptually understand
its role as "assembly language of the web," I have a hard time not seeing the language
that was written
in 10 days and named to deliberately confuse people.
GhostDoc, Help, and IntelliSence
I lead with all of this to help you understand the lens through which to read this
post. As a product of the strongly typed, object-oriented wave of programmers,
my subconscious still regards JavaScript as something of an afterthought, its dominance
notwithstanding. And so when I see advances in the JavaScript world, I tend
to think, "Oh, cool, you can do that with JavaScript too!"
I'll come back to that shortly. But first, I'd like to remind you of a prominent
GhostDoc feature. Specifically, I'm referring to the "Document This" capability.
With your cursor inside of a method or type, you can use this capability to generate
instant, well-formatted, XML doc comments. Thoroughly documented code sits just
a "Ctrl-Shift-D" away.
If you work with C# a lot and generate public APIs and/or help documentation, you
will love this capability. It doesn't absolve you of needing to add specific
contest. But it does give you a thorough base upon which to build. For
example, consider this method from my ChessTDD example
codebase.
/// <summary> /// Gets
the moves from. /// </summary> /// <param
name="startingLocation">The starting location.</param> /// <param
name="boardSize">Size of the board.</param> /// <returns>IEnumerable<BoardCoordinate>.</returns> public override IEnumerable<BoardCoordinate> GetMovesFrom(BoardCoordinate
startingLocation, int boardSize = Board.DefaultBoardSize)
{ var oneSquareAwayMoves = GetAllRadialMovesFrom(startingLocation, 1); return oneSquareAwayMoves.Where(bc => bc.IsCoordinateValidForBoardSize(boardSize));
}
I took that un-commented method and used GhostDoc to generate this. Now, I Should
probably update the summary, but I really don't see any other deficiencies here.
It nails the parameter names, and it even escapes the generic return type for readability.
It Works with JavaScript, Too
Now, as I've said earlier, I don't really play much in the JavaScript world.
I generally focus on application code and server side stuff, delving into the realm
of the client side browser only when necessary to get things done. So you can
imagine my reaction to learn that GhostDoc can do this with JavaScript too.
It can produce sophisticated XML doc comments that allow for use in generating help
and with IntelliSence.
Actually, if I really want to embarrass myself, I'll confess that my initial reaction
was, "JavaScript code has IntelliSence?" I think maybe I knew that, but I assumed
that such a thing would offer little value in a dynamically typed language.
Shows what I know. But then, once I got over that initial wave of ignorance,
I thought, "cool!" at the idea that you could generate documentation for JavaScript.
I then wondered if well-documented JavaScript was more or less rare than well documented
C#. That jury has yet to come back, as far as I know.
Let's Take a Look
Let's take a look at what GhostDoc actually does here. To demonstrate, I created a
branch of my ChessTDD project, to which I added an ASP MVC front end. This
plopped some JavaScript right in my lap, in the form of the default files in the "Scripts"
folder. To see this in action, I popped open modernizr-2.6.2.js and found a
method upon which to experiment.
ShivMethods sounded... interesting, in a prison yard sort of way.
As a publicly consumable library, it already has documentation. And, as I learned
by playing around, that documentation already gets picked up by IntelliSense.
But I figured I'd see what happened by using GhostDoc anyway, for comparison's sake.
So I lazily put my cursor randomly inside the method and fired away with a Ctrl-Shift-D.
Check it out.
As you can see, GhostDoc puts the comment inside of the method and behaves largely
as it would with C#. In case you're wondering about the stylistic difference,
GhostDoc does this because it's the
more Microsoft-preferred way. To underscore that, look what happens now
when we consume this method with IntelliSense.
As you can see, IntelliSense gives preference to the comments generated by GhostDoc.
The Takeaway
What's my main point here? I simply wanted to share that you can use GhostDoc
for JavaScript just as you can with C# or VB.
Forget about my crotchety ways and biases. Your JavaScript/client-side code
is every bit as important as what you write anywhere else. You should treat
it as such when documenting it, creating help for it, and making sure users know how
to consume your libraries. So make sure you've got Ctrl-Shift-D at the ready
when using Visual Studio, regardless of where the code gets executed.
Learn
more about how GhostDoc can help simplify your XML Comments, produce and maintain
quality help documentation.
About the Author
Erik Dietrich
I'm a passionate software developer and active blogger. Read about me at my
site. View
all posts by Erik Dietrich
|
|
|
|
|
|
|