diff --git a/examples/node/static_codegen/helloworld_grpc_pb.js b/examples/node/static_codegen/helloworld_grpc_pb.js
index 846f8b6bf575f2f134e05fa36f73e98f57553e08..7a8dce4d23ac74fc9c58a46237856c71484856eb 100644
--- a/examples/node/static_codegen/helloworld_grpc_pb.js
+++ b/examples/node/static_codegen/helloworld_grpc_pb.js
@@ -1,5 +1,35 @@
 // GENERATED CODE -- DO NOT EDIT!
 
+// Original file comments:
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
 'use strict';
 var grpc = require('grpc');
 var helloworld_pb = require('./helloworld_pb.js');
@@ -27,7 +57,9 @@ function deserialize_HelloRequest(buffer_arg) {
 }
 
 
+// The greeting service definition.
 var GreeterService = exports.GreeterService = {
+  // Sends a greeting
   sayHello: {
     path: '/helloworld.Greeter/SayHello',
     requestStream: false,
diff --git a/examples/node/static_codegen/helloworld_pb.js b/examples/node/static_codegen/helloworld_pb.js
index 6405bd90f10444bc4aa2141154801a18de8d584a..d1e50c90ea8e32abf27df7c90f605e54f9782fbf 100644
--- a/examples/node/static_codegen/helloworld_pb.js
+++ b/examples/node/static_codegen/helloworld_pb.js
@@ -61,7 +61,7 @@ proto.helloworld.HelloRequest.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -220,7 +220,7 @@ proto.helloworld.HelloReply.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
diff --git a/examples/node/static_codegen/route_guide/route_guide_grpc_pb.js b/examples/node/static_codegen/route_guide/route_guide_grpc_pb.js
index 1dd71331dbd3edcc0dad9ebed1075b9dedfa34aa..ce030c8a3bfa4fa6c02cc1a35a5dba2fb329f792 100644
--- a/examples/node/static_codegen/route_guide/route_guide_grpc_pb.js
+++ b/examples/node/static_codegen/route_guide/route_guide_grpc_pb.js
@@ -1,5 +1,35 @@
 // GENERATED CODE -- DO NOT EDIT!
 
+// Original file comments:
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
 'use strict';
 var grpc = require('grpc');
 var route_guide_pb = require('./route_guide_pb.js');
@@ -60,7 +90,14 @@ function deserialize_RouteSummary(buffer_arg) {
 }
 
 
+// Interface exported by the server.
 var RouteGuideService = exports.RouteGuideService = {
+  // A simple RPC.
+  //
+  // Obtains the feature at a given position.
+  //
+  // A feature with an empty name is returned if there's no feature at the given
+  // position.
   getFeature: {
     path: '/routeguide.RouteGuide/GetFeature',
     requestStream: false,
@@ -72,6 +109,12 @@ var RouteGuideService = exports.RouteGuideService = {
     responseSerialize: serialize_Feature,
     responseDeserialize: deserialize_Feature,
   },
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Features available within the given Rectangle.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field), as the rectangle may cover a large area and contain a
+  // huge number of features.
   listFeatures: {
     path: '/routeguide.RouteGuide/ListFeatures',
     requestStream: false,
@@ -83,6 +126,10 @@ var RouteGuideService = exports.RouteGuideService = {
     responseSerialize: serialize_Feature,
     responseDeserialize: deserialize_Feature,
   },
+  // A client-to-server streaming RPC.
+  //
+  // Accepts a stream of Points on a route being traversed, returning a
+  // RouteSummary when traversal is completed.
   recordRoute: {
     path: '/routeguide.RouteGuide/RecordRoute',
     requestStream: true,
@@ -94,6 +141,10 @@ var RouteGuideService = exports.RouteGuideService = {
     responseSerialize: serialize_RouteSummary,
     responseDeserialize: deserialize_RouteSummary,
   },
+  // A Bidirectional streaming RPC.
+  //
+  // Accepts a stream of RouteNotes sent while a route is being traversed,
+  // while receiving other RouteNotes (e.g. from other users).
   routeChat: {
     path: '/routeguide.RouteGuide/RouteChat',
     requestStream: true,
diff --git a/examples/node/static_codegen/route_guide/route_guide_pb.js b/examples/node/static_codegen/route_guide/route_guide_pb.js
index f604cd6d50821fd1633cb67b664a66af68a40efa..2e2f9a1da238138e8188d39d16a52fbc0f92354b 100644
--- a/examples/node/static_codegen/route_guide/route_guide_pb.js
+++ b/examples/node/static_codegen/route_guide/route_guide_pb.js
@@ -65,7 +65,7 @@ proto.routeguide.Point.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -251,7 +251,7 @@ proto.routeguide.Rectangle.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -453,7 +453,7 @@ proto.routeguide.Feature.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -647,7 +647,7 @@ proto.routeguide.RouteNote.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -843,7 +843,7 @@ proto.routeguide.RouteSummary.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
diff --git a/examples/ruby/lib/helloworld_services.rb b/examples/ruby/lib/helloworld_services.rb
index 7da45ebc6b202ec48ed60aca18e4e2539cf8875f..fbec6677942b047fcd85a11edb8828777e8a5a3c 100644
--- a/examples/ruby/lib/helloworld_services.rb
+++ b/examples/ruby/lib/helloworld_services.rb
@@ -1,13 +1,42 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: helloworld.proto for package 'helloworld'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
 
 require 'grpc'
 require 'helloworld'
 
 module Helloworld
   module Greeter
-
-    # TODO: add proto service documentation here
+    # The greeting service definition.
     class Service
 
       include GRPC::GenericService
@@ -16,6 +45,7 @@ module Helloworld
       self.unmarshal_class_method = :decode
       self.service_name = 'helloworld.Greeter'
 
+      # Sends a greeting
       rpc :SayHello, HelloRequest, HelloReply
     end
 
diff --git a/examples/ruby/lib/route_guide_services.rb b/examples/ruby/lib/route_guide_services.rb
index 082daef7543f5922c83bb14a30bd088acf6e4ecc..d8f123dd95b122aa03838ceaf9bb7e7be4890ce3 100644
--- a/examples/ruby/lib/route_guide_services.rb
+++ b/examples/ruby/lib/route_guide_services.rb
@@ -1,13 +1,42 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: route_guide.proto for package 'routeguide'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
 
 require 'grpc'
 require 'route_guide'
 
 module Routeguide
   module RouteGuide
-
-    # TODO: add proto service documentation here
+    # Interface exported by the server.
     class Service
 
       include GRPC::GenericService
@@ -16,9 +45,29 @@ module Routeguide
       self.unmarshal_class_method = :decode
       self.service_name = 'routeguide.RouteGuide'
 
+      # A simple RPC.
+      #
+      # Obtains the feature at a given position.
+      #
+      # A feature with an empty name is returned if there's no feature at the given
+      # position.
       rpc :GetFeature, Point, Feature
+      # A server-to-client streaming RPC.
+      #
+      # Obtains the Features available within the given Rectangle.  Results are
+      # streamed rather than returned at once (e.g. in a response message with a
+      # repeated field), as the rectangle may cover a large area and contain a
+      # huge number of features.
       rpc :ListFeatures, Rectangle, stream(Feature)
+      # A client-to-server streaming RPC.
+      #
+      # Accepts a stream of Points on a route being traversed, returning a
+      # RouteSummary when traversal is completed.
       rpc :RecordRoute, stream(Point), RouteSummary
+      # A Bidirectional streaming RPC.
+      #
+      # Accepts a stream of RouteNotes sent while a route is being traversed,
+      # while receiving other RouteNotes (e.g. from other users).
       rpc :RouteChat, stream(RouteNote), stream(RouteNote)
     end
 
diff --git a/src/compiler/cpp_generator_helpers.h b/src/compiler/cpp_generator_helpers.h
index be68cbe695aeeba496b04dd182a50b35c5d8055f..87e278f1b9fe5a0b71067994a42ff0f9793acfb2 100644
--- a/src/compiler/cpp_generator_helpers.h
+++ b/src/compiler/cpp_generator_helpers.h
@@ -65,6 +65,13 @@ inline grpc::string ClassName(const grpc::protobuf::Descriptor *descriptor,
   }
 }
 
+// Get leading or trailing comments in a string. Comment lines start with "// ".
+// Leading detached comments are put in in front of leading comments.
+template <typename DescriptorType>
+inline grpc::string GetCppComments(const DescriptorType *desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
 }  // namespace grpc_cpp_generator
 
 #endif  // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H
diff --git a/src/compiler/cpp_plugin.cc b/src/compiler/cpp_plugin.cc
index 0ec183e474e593ff4ecb79929987af3c10bb5789..fc0296cd282edb3ea8e5c81cbc191a282ef89560 100644
--- a/src/compiler/cpp_plugin.cc
+++ b/src/compiler/cpp_plugin.cc
@@ -43,7 +43,7 @@
 #include "src/compiler/cpp_generator_helpers.h"
 #include "src/compiler/generator_helpers.h"
 
-using grpc_generator::GetCppComments;
+using grpc_cpp_generator::GetCppComments;
 
 class ProtoBufMethod : public grpc_cpp_generator::Method {
  public:
diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc
index 29c359c539572fdbb74e640140d021977fb68439..484fa3cdcc4357516ed92091845978e40f06e8f0 100644
--- a/src/compiler/csharp_generator.cc
+++ b/src/compiler/csharp_generator.cc
@@ -52,7 +52,6 @@ using grpc::protobuf::MethodDescriptor;
 using grpc::protobuf::io::Printer;
 using grpc::protobuf::io::StringOutputStream;
 using grpc_generator::MethodType;
-using grpc_generator::GetCppComments;
 using grpc_generator::GetMethodType;
 using grpc_generator::METHODTYPE_NO_STREAMING;
 using grpc_generator::METHODTYPE_CLIENT_STREAMING;
@@ -659,7 +658,7 @@ grpc::string GetServices(const FileDescriptor *file, bool generate_client,
     out.Print("// source: $filename$\n", "filename", file->name());
 
     // use C++ style as there are no file-level XML comments in .NET
-    grpc::string leading_comments = GetCppComments(file, true);
+    grpc::string leading_comments = GetCsharpComments(file, true);
     if (!leading_comments.empty()) {
       out.Print("// Original file comments:\n");
       out.Print(leading_comments.c_str());
diff --git a/src/compiler/csharp_generator_helpers.h b/src/compiler/csharp_generator_helpers.h
index 5639ea058b80a4da321c3c9bc3fabf294e0969c5..9bdf6fb53562064bf8c99be9cde4bc6060c0fe6d 100644
--- a/src/compiler/csharp_generator_helpers.h
+++ b/src/compiler/csharp_generator_helpers.h
@@ -45,6 +45,13 @@ inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file,
   return true;
 }
 
+// Get leading or trailing comments in a string. Comment lines start with "// ".
+// Leading detached comments are put in in front of leading comments.
+template <typename DescriptorType>
+inline grpc::string GetCsharpComments(const DescriptorType *desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
 }  // namespace grpc_csharp_generator
 
 #endif  // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H
diff --git a/src/compiler/generator_helpers.h b/src/compiler/generator_helpers.h
index bd077cf7983d957887a794110f2a7fb05a2af2ca..53391bc41ab10b670c8f74c9a335685dc0ec8778 100644
--- a/src/compiler/generator_helpers.h
+++ b/src/compiler/generator_helpers.h
@@ -265,10 +265,10 @@ inline grpc::string GenerateCommentsWithPrefix(
   return oss.str();
 }
 
-// Get leading or trailing comments in a string. Comment lines start with "// ".
-// Leading detached comments are put in in front of leading comments.
 template <typename DescriptorType>
-inline grpc::string GetCppComments(const DescriptorType *desc, bool leading) {
+inline grpc::string GetPrefixedComments(const DescriptorType *desc,
+                                        bool leading,
+                                        const grpc::string &prefix) {
   std::vector<grpc::string> out;
   if (leading) {
     grpc_generator::GetComment(
@@ -281,7 +281,7 @@ inline grpc::string GetCppComments(const DescriptorType *desc, bool leading) {
     grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
                                &out);
   }
-  return GenerateCommentsWithPrefix(out, "//");
+  return GenerateCommentsWithPrefix(out, prefix);
 }
 
 }  // namespace grpc_generator
diff --git a/src/compiler/node_generator.cc b/src/compiler/node_generator.cc
index 822622cccf9c553adbc6bd846f85691b0ed91f80..986b97c26e98fd519433305d7bebdd02296d2134 100644
--- a/src/compiler/node_generator.cc
+++ b/src/compiler/node_generator.cc
@@ -181,62 +181,67 @@ void PrintMethod(const MethodDescriptor *method, Printer *out) {
 // Prints out the service descriptor object
 void PrintService(const ServiceDescriptor *service, Printer *out) {
   map<grpc::string, grpc::string> template_vars;
+  out->Print(GetNodeComments(service, true).c_str());
   template_vars["name"] = service->name();
   out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n");
   out->Indent();
   for (int i = 0; i < service->method_count(); i++) {
     grpc::string method_name = grpc_generator::LowercaseFirstLetter(
         service->method(i)->name());
+    out->Print(GetNodeComments(service->method(i), true).c_str());
     out->Print("$method_name$: ",
                "method_name", method_name);
     PrintMethod(service->method(i), out);
     out->Print(",\n");
+    out->Print(GetNodeComments(service->method(i), false).c_str());
   }
   out->Outdent();
   out->Print("};\n\n");
   out->Print(template_vars, "exports.$name$Client = "
              "grpc.makeGenericClientConstructor($name$Service);\n");
+  out->Print(GetNodeComments(service, false).c_str());
 }
 
-}
-
-grpc::string GetImports(const FileDescriptor *file) {
-  grpc::string output;
-  {
-    StringOutputStream output_stream(&output);
-    Printer out(&output_stream, '$');
-
-    if (file->service_count() == 0) {
-      return output;
-    }
-
-    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
+void PrintImports(const FileDescriptor *file, Printer *out) {
+  out->Print("var grpc = require('grpc');\n");
+  if (file->message_type_count() > 0) {
+    grpc::string file_path = GetRelativePath(file->name(),
+                                             GetJSMessageFilename(
+                                                 file->name()));
+    out->Print("var $module_alias$ = require('$file_path$');\n",
+               "module_alias", ModuleAlias(file->name()),
+               "file_path", file_path);
+  }
 
-    out.Print("'use strict';\n");
+  for (int i = 0; i < file->dependency_count(); i++) {
+    grpc::string file_path = GetRelativePath(
+        file->name(), GetJSMessageFilename(file->dependency(i)->name()));
+    out->Print("var $module_alias$ = require('$file_path$');\n",
+               "module_alias", ModuleAlias(file->dependency(i)->name()),
+               "file_path", file_path);
+  }
+  out->Print("\n");
+}
 
-    out.Print("var grpc = require('grpc');\n");
-    if (file->message_type_count() > 0) {
-      grpc::string file_path = GetRelativePath(file->name(),
-                                               GetJSMessageFilename(
-                                                   file->name()));
-      out.Print("var $module_alias$ = require('$file_path$');\n",
-                "module_alias", ModuleAlias(file->name()),
-                "file_path", file_path);
-    }
+void PrintTransformers(const FileDescriptor *file, Printer *out) {
+  map<grpc::string, const Descriptor*> messages = GetAllMessages(file);
+  for (std::map<grpc::string, const Descriptor*>::iterator it =
+           messages.begin();
+       it != messages.end(); it++) {
+    PrintMessageTransformer(it->second, out);
+  }
+  out->Print("\n");
+}
 
-    for (int i = 0; i < file->dependency_count(); i++) {
-      grpc::string file_path = GetRelativePath(
-          file->name(), GetJSMessageFilename(file->dependency(i)->name()));
-      out.Print("var $module_alias$ = require('$file_path$');\n",
-                "module_alias", ModuleAlias(file->dependency(i)->name()),
-                "file_path", file_path);
-    }
-    out.Print("\n");
+void PrintServices(const FileDescriptor *file, Printer *out) {
+  for (int i = 0; i < file->service_count(); i++) {
+    PrintService(file->service(i), out);
   }
-  return output;
 }
 
-grpc::string GetTransformers(const FileDescriptor *file) {
+}
+
+grpc::string GenerateFile(const FileDescriptor *file) {
   grpc::string output;
   {
     StringOutputStream output_stream(&output);
@@ -245,31 +250,23 @@ grpc::string GetTransformers(const FileDescriptor *file) {
     if (file->service_count() == 0) {
       return output;
     }
+    out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n");
 
-    map<grpc::string, const Descriptor*> messages = GetAllMessages(file);
-    for (std::map<grpc::string, const Descriptor*>::iterator it =
-             messages.begin();
-         it != messages.end(); it++) {
-      PrintMessageTransformer(it->second, &out);
+    grpc::string leading_comments = GetNodeComments(file, true);
+    if (!leading_comments.empty()) {
+      out.Print("// Original file comments:\n");
+      out.Print(leading_comments.c_str());
     }
-    out.Print("\n");
-  }
-  return output;
-}
 
-grpc::string GetServices(const FileDescriptor *file) {
-  grpc::string output;
-  {
-    StringOutputStream output_stream(&output);
-    Printer out(&output_stream, '$');
+    out.Print("'use strict';\n");
 
-    if (file->service_count() == 0) {
-      return output;
-    }
+    PrintImports(file, &out);
 
-    for (int i = 0; i < file->service_count(); i++) {
-      PrintService(file->service(i), &out);
-    }
+    PrintTransformers(file, &out);
+
+    PrintServices(file, &out);
+
+    out.Print(GetNodeComments(file, false).c_str());
   }
   return output;
 }
diff --git a/src/compiler/node_generator.h b/src/compiler/node_generator.h
index 249a0d011f88bac53353e8297ec8b7fed8470f67..d7765e2d2805d82a84d15ce062167e91e45079d8 100644
--- a/src/compiler/node_generator.h
+++ b/src/compiler/node_generator.h
@@ -38,11 +38,7 @@
 
 namespace grpc_node_generator {
 
-grpc::string GetImports(const grpc::protobuf::FileDescriptor *file);
-
-grpc::string GetTransformers(const grpc::protobuf::FileDescriptor *file);
-
-grpc::string GetServices(const grpc::protobuf::FileDescriptor *file);
+grpc::string GenerateFile(const grpc::protobuf::FileDescriptor *file);
 
 }  // namespace grpc_node_generator
 
diff --git a/src/compiler/node_generator_helpers.h b/src/compiler/node_generator_helpers.h
index f41a2bcf59a7afcd76b3c74f4cbd8604c190793f..5862772841c5e832a7a32ed7124e1101d13a3323 100644
--- a/src/compiler/node_generator_helpers.h
+++ b/src/compiler/node_generator_helpers.h
@@ -45,6 +45,13 @@ inline grpc::string GetJSServiceFilename(const grpc::string& filename) {
   return grpc_generator::StripProto(filename) + "_grpc_pb.js";
 }
 
+// Get leading or trailing comments in a string. Comment lines start with "// ".
+// Leading detached comments are put in in front of leading comments.
+template <typename DescriptorType>
+inline grpc::string GetNodeComments(const DescriptorType *desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "//");
+}
+
 }  // namespace grpc_node_generator
 
 #endif  // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H
diff --git a/src/compiler/node_plugin.cc b/src/compiler/node_plugin.cc
index ac5ced35589bf557cf2e360ad0315c89c2b20bfa..39dfa77b8d5aca2e2cec2fb9d96c02a07bc0a2b8 100644
--- a/src/compiler/node_plugin.cc
+++ b/src/compiler/node_plugin.cc
@@ -39,10 +39,8 @@
 #include "src/compiler/node_generator.h"
 #include "src/compiler/node_generator_helpers.h"
 
-using grpc_node_generator::GetImports;
+using grpc_node_generator::GenerateFile;
 using grpc_node_generator::GetJSServiceFilename;
-using grpc_node_generator::GetServices;
-using grpc_node_generator::GetTransformers;
 
 class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
  public:
@@ -53,9 +51,7 @@ class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
                 const grpc::string &parameter,
                 grpc::protobuf::compiler::GeneratorContext *context,
                 grpc::string *error) const {
-    grpc::string code = GetImports(file) +
-        GetTransformers(file) +
-        GetServices(file);
+    grpc::string code = GenerateFile(file);
     if (code.size() == 0) {
       return true;
     }
diff --git a/src/compiler/ruby_generator.cc b/src/compiler/ruby_generator.cc
index 936a186beb5a4d097dbaaa9c2e5bb7b6f7560aa1..1501c3f3e01b050ae2b2be9afbb3d64e68e859de 100644
--- a/src/compiler/ruby_generator.cc
+++ b/src/compiler/ruby_generator.cc
@@ -66,7 +66,9 @@ void PrintMethod(const MethodDescriptor *method, const grpc::string &package,
   std::map<grpc::string, grpc::string> method_vars =
       ListToDict({"mth.name", method->name(), "input.type", input_type,
                   "output.type", output_type, });
+  out->Print(GetRubyComments(method, true).c_str());
   out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n");
+  out->Print(GetRubyComments(method, false).c_str());
 }
 
 // Prints out the service using the ruby gRPC DSL.
@@ -82,12 +84,7 @@ void PrintService(const ServiceDescriptor *service, const grpc::string &package,
   out->Print(module_vars, "module $module.name$\n");
   out->Indent();
 
-  // TODO(temiola): add documentation
-  grpc::string doc = "TODO: add proto service documentation here";
-  std::map<grpc::string, grpc::string> template_vars =
-      ListToDict({"Documentation", doc, });
-  out->Print("\n");
-  out->Print(template_vars, "# $Documentation$\n");
+  out->Print(GetRubyComments(service, true).c_str());
   out->Print("class Service\n");
 
   // Write the indented class body.
@@ -113,6 +110,7 @@ void PrintService(const ServiceDescriptor *service, const grpc::string &package,
   // End the service module
   out->Outdent();
   out->Print("end\n");
+  out->Print(GetRubyComments(service, false).c_str());
 }
 
 }  // namespace
@@ -138,6 +136,12 @@ grpc::string GetServices(const FileDescriptor *file) {
     out.Print(header_comment_vars,
               "# Source: $file.name$ for package '$file.package$'\n");
 
+    grpc::string leading_comments = GetRubyComments(file, true);
+    if (!leading_comments.empty()) {
+      out.Print("# Original file comments:\n");
+      out.Print(leading_comments.c_str());
+    }
+
     out.Print("\n");
     out.Print("require 'grpc'\n");
     // Write out require statemment to import the separately generated file
@@ -164,6 +168,8 @@ grpc::string GetServices(const FileDescriptor *file) {
       out.Outdent();
       out.Print("end\n");
     }
+
+    out.Print(GetRubyComments(file, false).c_str());
   }
   return output;
 }
diff --git a/src/compiler/ruby_generator_helpers-inl.h b/src/compiler/ruby_generator_helpers-inl.h
index 9da7cab3c7f3dd5d33b023f67fc26a50cbd8686d..ff6939ed9fed0a0e5bc20944e61e97e26a01ecaf 100644
--- a/src/compiler/ruby_generator_helpers-inl.h
+++ b/src/compiler/ruby_generator_helpers-inl.h
@@ -35,6 +35,7 @@
 #define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H
 
 #include "src/compiler/config.h"
+#include "src/compiler/generator_helpers.h"
 #include "src/compiler/ruby_generator_string-inl.h"
 
 namespace grpc_ruby_generator {
@@ -60,6 +61,13 @@ inline grpc::string MessagesRequireName(
   return Replace(file->name(), ".proto", "");
 }
 
+// Get leading or trailing comments in a string. Comment lines start with "# ".
+// Leading detached comments are put in in front of leading comments.
+template <typename DescriptorType>
+inline grpc::string GetRubyComments(const DescriptorType *desc, bool leading) {
+  return grpc_generator::GetPrefixedComments(desc, leading, "#");
+}
+
 }  // namespace grpc_ruby_generator
 
 #endif  // GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H
diff --git a/src/node/test/math/math_grpc_pb.js b/src/node/test/math/math_grpc_pb.js
index 083ed669137e436ad9d994f5107ba22d7400653e..17a4bf7243f43002c37dd26bca0dfd827339cef7 100644
--- a/src/node/test/math/math_grpc_pb.js
+++ b/src/node/test/math/math_grpc_pb.js
@@ -1,94 +1,135 @@
 // GENERATED CODE -- DO NOT EDIT!
 
+// Original file comments:
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
 'use strict';
 var grpc = require('grpc');
-var math_pb = require('./math_pb.js');
+var math_math_pb = require('../math/math_pb.js');
 
 function serialize_DivArgs(arg) {
-  if (!(arg instanceof math_pb.DivArgs)) {
+  if (!(arg instanceof math_math_pb.DivArgs)) {
     throw new Error('Expected argument of type DivArgs');
   }
   return new Buffer(arg.serializeBinary());
 }
 
 function deserialize_DivArgs(buffer_arg) {
-  return math_pb.DivArgs.deserializeBinary(new Uint8Array(buffer_arg));
+  return math_math_pb.DivArgs.deserializeBinary(new Uint8Array(buffer_arg));
 }
 
 function serialize_DivReply(arg) {
-  if (!(arg instanceof math_pb.DivReply)) {
+  if (!(arg instanceof math_math_pb.DivReply)) {
     throw new Error('Expected argument of type DivReply');
   }
   return new Buffer(arg.serializeBinary());
 }
 
 function deserialize_DivReply(buffer_arg) {
-  return math_pb.DivReply.deserializeBinary(new Uint8Array(buffer_arg));
+  return math_math_pb.DivReply.deserializeBinary(new Uint8Array(buffer_arg));
 }
 
 function serialize_FibArgs(arg) {
-  if (!(arg instanceof math_pb.FibArgs)) {
+  if (!(arg instanceof math_math_pb.FibArgs)) {
     throw new Error('Expected argument of type FibArgs');
   }
   return new Buffer(arg.serializeBinary());
 }
 
 function deserialize_FibArgs(buffer_arg) {
-  return math_pb.FibArgs.deserializeBinary(new Uint8Array(buffer_arg));
+  return math_math_pb.FibArgs.deserializeBinary(new Uint8Array(buffer_arg));
 }
 
 function serialize_Num(arg) {
-  if (!(arg instanceof math_pb.Num)) {
+  if (!(arg instanceof math_math_pb.Num)) {
     throw new Error('Expected argument of type Num');
   }
   return new Buffer(arg.serializeBinary());
 }
 
 function deserialize_Num(buffer_arg) {
-  return math_pb.Num.deserializeBinary(new Uint8Array(buffer_arg));
+  return math_math_pb.Num.deserializeBinary(new Uint8Array(buffer_arg));
 }
 
 
 var MathService = exports.MathService = {
+  // Div divides args.dividend by args.divisor and returns the quotient and
+  // remainder.
   div: {
     path: '/math.Math/Div',
     requestStream: false,
     responseStream: false,
-    requestType: math_pb.DivArgs,
-    responseType: math_pb.DivReply,
+    requestType: math_math_pb.DivArgs,
+    responseType: math_math_pb.DivReply,
     requestSerialize: serialize_DivArgs,
     requestDeserialize: deserialize_DivArgs,
     responseSerialize: serialize_DivReply,
     responseDeserialize: deserialize_DivReply,
   },
+  // DivMany accepts an arbitrary number of division args from the client stream
+  // and sends back the results in the reply stream.  The stream continues until
+  // the client closes its end; the server does the same after sending all the
+  // replies.  The stream ends immediately if either end aborts.
   divMany: {
     path: '/math.Math/DivMany',
     requestStream: true,
     responseStream: true,
-    requestType: math_pb.DivArgs,
-    responseType: math_pb.DivReply,
+    requestType: math_math_pb.DivArgs,
+    responseType: math_math_pb.DivReply,
     requestSerialize: serialize_DivArgs,
     requestDeserialize: deserialize_DivArgs,
     responseSerialize: serialize_DivReply,
     responseDeserialize: deserialize_DivReply,
   },
+  // Fib generates numbers in the Fibonacci sequence.  If args.limit > 0, Fib
+  // generates up to limit numbers; otherwise it continues until the call is
+  // canceled.  Unlike Fib above, Fib has no final FibReply.
   fib: {
     path: '/math.Math/Fib',
     requestStream: false,
     responseStream: true,
-    requestType: math_pb.FibArgs,
-    responseType: math_pb.Num,
+    requestType: math_math_pb.FibArgs,
+    responseType: math_math_pb.Num,
     requestSerialize: serialize_FibArgs,
     requestDeserialize: deserialize_FibArgs,
     responseSerialize: serialize_Num,
     responseDeserialize: deserialize_Num,
   },
+  // Sum sums a stream of numbers, returning the final result once the stream
+  // is closed.
   sum: {
     path: '/math.Math/Sum',
     requestStream: true,
     responseStream: false,
-    requestType: math_pb.Num,
-    responseType: math_pb.Num,
+    requestType: math_math_pb.Num,
+    responseType: math_math_pb.Num,
     requestSerialize: serialize_Num,
     requestDeserialize: deserialize_Num,
     responseSerialize: serialize_Num,
diff --git a/src/node/test/math/math_pb.js b/src/node/test/math/math_pb.js
index 3489143bec5f0324af61162fc2e5e3a2d8909c72..ccc05c6e06dca2d4b07dd5ee8dce6b1e9799066f 100644
--- a/src/node/test/math/math_pb.js
+++ b/src/node/test/math/math_pb.js
@@ -65,7 +65,7 @@ proto.math.DivArgs.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -251,7 +251,7 @@ proto.math.DivReply.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -436,7 +436,7 @@ proto.math.FibArgs.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -595,7 +595,7 @@ proto.math.Num.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
@@ -754,7 +754,7 @@ proto.math.FibReply.toObject = function(includeInstance, msg) {
   };
 
   if (includeInstance) {
-    obj.$jspbMessageInstance = msg
+    obj.$jspbMessageInstance = msg;
   }
   return obj;
 };
diff --git a/src/ruby/bin/math_services.rb b/src/ruby/bin/math_services.rb
index 2d482129c2afa1dca6d4962951d2f26aebc63c58..34c36abddae47a29f1815a38808aca76e2e98f9c 100755
--- a/src/ruby/bin/math_services.rb
+++ b/src/ruby/bin/math_services.rb
@@ -1,13 +1,41 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: math.proto for package 'math'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
 
 require 'grpc'
 require 'math'
 
 module Math
   module Math
-
-    # TODO: add proto service documentation here
     class Service
 
       include GRPC::GenericService
@@ -16,9 +44,20 @@ module Math
       self.unmarshal_class_method = :decode
       self.service_name = 'math.Math'
 
+      # Div divides args.dividend by args.divisor and returns the quotient and
+      # remainder.
       rpc :Div, DivArgs, DivReply
+      # DivMany accepts an arbitrary number of division args from the client stream
+      # and sends back the results in the reply stream.  The stream continues until
+      # the client closes its end; the server does the same after sending all the
+      # replies.  The stream ends immediately if either end aborts.
       rpc :DivMany, stream(DivArgs), stream(DivReply)
+      # Fib generates numbers in the Fibonacci sequence.  If args.limit > 0, Fib
+      # generates up to limit numbers; otherwise it continues until the call is
+      # canceled.  Unlike Fib above, Fib has no final FibReply.
       rpc :Fib, FibArgs, stream(Num)
+      # Sum sums a stream of numbers, returning the final result once the stream
+      # is closed.
       rpc :Sum, stream(Num), Num
     end
 
diff --git a/src/ruby/pb/grpc/health/v1/health_services.rb b/src/ruby/pb/grpc/health/v1/health_services.rb
index cb79b20437f573b40f9c3f6265eec722db4e87c7..68a3956f54a09c6f6c0ce40cf0c98810087e05b7 100644
--- a/src/ruby/pb/grpc/health/v1/health_services.rb
+++ b/src/ruby/pb/grpc/health/v1/health_services.rb
@@ -1,5 +1,35 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: grpc/health/v1/health.proto for package 'grpc.health.v1'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
 
 require 'grpc'
 require 'grpc/health/v1/health'
@@ -8,8 +38,6 @@ module Grpc
   module Health
     module V1
       module Health
-
-        # TODO: add proto service documentation here
         class Service
 
           include GRPC::GenericService
diff --git a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb
index 9f6e7e0e427759f20638a14036254250bae0db52..eb523ffa6f0ac82355b60f5952b5fb145f9cc073 100644
--- a/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb
+++ b/src/ruby/pb/grpc/testing/duplicate/echo_duplicate_services.rb
@@ -1,15 +1,45 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
-# Source: src/proto/grpc/testing/duplicate/echo_duplicate.proto for package 'grpc.testing.duplicate'
+# Source: grpc/testing/duplicate/echo_duplicate.proto for package 'grpc.testing.duplicate'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# This is a partial copy of echo.proto with a different package name.
+#
 
 require 'grpc'
-require 'src/proto/grpc/testing/duplicate/echo_duplicate'
+require 'grpc/testing/duplicate/echo_duplicate'
 
 module Grpc
   module Testing
     module Duplicate
       module EchoTestService
-
-        # TODO: add proto service documentation here
         class Service
 
           include GRPC::GenericService
diff --git a/src/ruby/pb/grpc/testing/metrics_services.rb b/src/ruby/pb/grpc/testing/metrics_services.rb
index f5778bbbb191429587357c33726e19290382142a..467b7b3ee509b6df5a51d3bd37b0236178688eda 100644
--- a/src/ruby/pb/grpc/testing/metrics_services.rb
+++ b/src/ruby/pb/grpc/testing/metrics_services.rb
@@ -1,5 +1,41 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: grpc/testing/metrics.proto for package 'grpc.testing'
+# Original file comments:
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Contains the definitions for a metrics service and the type of metrics
+# exposed by the service.
+#
+# Currently, 'Gauge' (i.e a metric that represents the measured value of
+# something at an instant of time) is the only metric type supported by the
+# service.
 
 require 'grpc'
 require 'grpc/testing/metrics'
@@ -7,8 +43,6 @@ require 'grpc/testing/metrics'
 module Grpc
   module Testing
     module MetricsService
-
-      # TODO: add proto service documentation here
       class Service
 
         include GRPC::GenericService
@@ -17,7 +51,10 @@ module Grpc
         self.unmarshal_class_method = :decode
         self.service_name = 'grpc.testing.MetricsService'
 
+        # Returns the values of all the gauges that are currently being maintained by
+        # the service
         rpc :GetAllGauges, EmptyMessage, stream(GaugeResponse)
+        # Returns the value of one gauge
         rpc :GetGauge, GaugeRequest, GaugeResponse
       end
 
diff --git a/src/ruby/pb/src/proto/grpc/testing/empty.rb b/src/ruby/pb/src/proto/grpc/testing/empty.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9c2568d6053f14fa163bc19da766c74414ecf43b
--- /dev/null
+++ b/src/ruby/pb/src/proto/grpc/testing/empty.rb
@@ -0,0 +1,15 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: src/proto/grpc/testing/empty.proto
+
+require 'google/protobuf'
+
+Google::Protobuf::DescriptorPool.generated_pool.build do
+  add_message "grpc.testing.Empty" do
+  end
+end
+
+module Grpc
+  module Testing
+    Empty = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.Empty").msgclass
+  end
+end
diff --git a/src/ruby/pb/src/proto/grpc/testing/messages.rb b/src/ruby/pb/src/proto/grpc/testing/messages.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2bdfe0eade338c0d0101e28a3640aea113b1fc7c
--- /dev/null
+++ b/src/ruby/pb/src/proto/grpc/testing/messages.rb
@@ -0,0 +1,84 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: src/proto/grpc/testing/messages.proto
+
+require 'google/protobuf'
+
+Google::Protobuf::DescriptorPool.generated_pool.build do
+  add_message "grpc.testing.Payload" do
+    optional :type, :enum, 1, "grpc.testing.PayloadType"
+    optional :body, :bytes, 2
+  end
+  add_message "grpc.testing.EchoStatus" do
+    optional :code, :int32, 1
+    optional :message, :string, 2
+  end
+  add_message "grpc.testing.SimpleRequest" do
+    optional :response_type, :enum, 1, "grpc.testing.PayloadType"
+    optional :response_size, :int32, 2
+    optional :payload, :message, 3, "grpc.testing.Payload"
+    optional :fill_username, :bool, 4
+    optional :fill_oauth_scope, :bool, 5
+    optional :response_compression, :enum, 6, "grpc.testing.CompressionType"
+    optional :response_status, :message, 7, "grpc.testing.EchoStatus"
+  end
+  add_message "grpc.testing.SimpleResponse" do
+    optional :payload, :message, 1, "grpc.testing.Payload"
+    optional :username, :string, 2
+    optional :oauth_scope, :string, 3
+  end
+  add_message "grpc.testing.StreamingInputCallRequest" do
+    optional :payload, :message, 1, "grpc.testing.Payload"
+  end
+  add_message "grpc.testing.StreamingInputCallResponse" do
+    optional :aggregated_payload_size, :int32, 1
+  end
+  add_message "grpc.testing.ResponseParameters" do
+    optional :size, :int32, 1
+    optional :interval_us, :int32, 2
+  end
+  add_message "grpc.testing.StreamingOutputCallRequest" do
+    optional :response_type, :enum, 1, "grpc.testing.PayloadType"
+    repeated :response_parameters, :message, 2, "grpc.testing.ResponseParameters"
+    optional :payload, :message, 3, "grpc.testing.Payload"
+    optional :response_compression, :enum, 6, "grpc.testing.CompressionType"
+    optional :response_status, :message, 7, "grpc.testing.EchoStatus"
+  end
+  add_message "grpc.testing.StreamingOutputCallResponse" do
+    optional :payload, :message, 1, "grpc.testing.Payload"
+  end
+  add_message "grpc.testing.ReconnectParams" do
+    optional :max_reconnect_backoff_ms, :int32, 1
+  end
+  add_message "grpc.testing.ReconnectInfo" do
+    optional :passed, :bool, 1
+    repeated :backoff_ms, :int32, 2
+  end
+  add_enum "grpc.testing.PayloadType" do
+    value :COMPRESSABLE, 0
+    value :UNCOMPRESSABLE, 1
+    value :RANDOM, 2
+  end
+  add_enum "grpc.testing.CompressionType" do
+    value :NONE, 0
+    value :GZIP, 1
+    value :DEFLATE, 2
+  end
+end
+
+module Grpc
+  module Testing
+    Payload = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.Payload").msgclass
+    EchoStatus = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.EchoStatus").msgclass
+    SimpleRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.SimpleRequest").msgclass
+    SimpleResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.SimpleResponse").msgclass
+    StreamingInputCallRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingInputCallRequest").msgclass
+    StreamingInputCallResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingInputCallResponse").msgclass
+    ResponseParameters = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ResponseParameters").msgclass
+    StreamingOutputCallRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingOutputCallRequest").msgclass
+    StreamingOutputCallResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.StreamingOutputCallResponse").msgclass
+    ReconnectParams = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectParams").msgclass
+    ReconnectInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.ReconnectInfo").msgclass
+    PayloadType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.PayloadType").enummodule
+    CompressionType = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.testing.CompressionType").enummodule
+  end
+end
diff --git a/src/ruby/pb/src/proto/grpc/testing/test.rb b/src/ruby/pb/src/proto/grpc/testing/test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..245b5ce00cdc66e273888b96d0980400ecb01152
--- /dev/null
+++ b/src/ruby/pb/src/proto/grpc/testing/test.rb
@@ -0,0 +1,14 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: src/proto/grpc/testing/test.proto
+
+require 'google/protobuf'
+
+require 'src/proto/grpc/testing/empty'
+require 'src/proto/grpc/testing/messages'
+Google::Protobuf::DescriptorPool.generated_pool.build do
+end
+
+module Grpc
+  module Testing
+  end
+end
diff --git a/src/ruby/pb/src/proto/grpc/testing/test_services.rb b/src/ruby/pb/src/proto/grpc/testing/test_services.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2652de5e6d255a3cf4247aafc18587cd661a9884
--- /dev/null
+++ b/src/ruby/pb/src/proto/grpc/testing/test_services.rb
@@ -0,0 +1,110 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# Source: src/proto/grpc/testing/test.proto for package 'grpc.testing'
+# Original file comments:
+# Copyright 2015-2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# An integration test service that covers all the method signature permutations
+# of unary/streaming requests/responses.
+#
+
+require 'grpc'
+require 'src/proto/grpc/testing/test'
+
+module Grpc
+  module Testing
+    module TestService
+      # A simple service to test the various types of RPCs and experiment with
+      # performance with various types of payload.
+      class Service
+
+        include GRPC::GenericService
+
+        self.marshal_class_method = :encode
+        self.unmarshal_class_method = :decode
+        self.service_name = 'grpc.testing.TestService'
+
+        # One empty request followed by one empty response.
+        rpc :EmptyCall, Empty, Empty
+        # One request followed by one response.
+        rpc :UnaryCall, SimpleRequest, SimpleResponse
+        # One request followed by a sequence of responses (streamed download).
+        # The server returns the payload with client desired type and sizes.
+        rpc :StreamingOutputCall, StreamingOutputCallRequest, stream(StreamingOutputCallResponse)
+        # A sequence of requests followed by one response (streamed upload).
+        # The server returns the aggregated size of client payload as the result.
+        rpc :StreamingInputCall, stream(StreamingInputCallRequest), StreamingInputCallResponse
+        # A sequence of requests with each request served by the server immediately.
+        # As one request could lead to multiple responses, this interface
+        # demonstrates the idea of full duplexing.
+        rpc :FullDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
+        # A sequence of requests followed by a sequence of responses.
+        # The server buffers all the client requests and then serves them in order. A
+        # stream of responses are returned to the client when the server starts with
+        # first request.
+        rpc :HalfDuplexCall, stream(StreamingOutputCallRequest), stream(StreamingOutputCallResponse)
+      end
+
+      Stub = Service.rpc_stub_class
+    end
+    module UnimplementedService
+      # A simple service NOT implemented at servers so clients can test for
+      # that case.
+      class Service
+
+        include GRPC::GenericService
+
+        self.marshal_class_method = :encode
+        self.unmarshal_class_method = :decode
+        self.service_name = 'grpc.testing.UnimplementedService'
+
+        # A call that no server should implement
+        rpc :UnimplementedCall, Empty, Empty
+      end
+
+      Stub = Service.rpc_stub_class
+    end
+    module ReconnectService
+      # A service used to control reconnect server.
+      class Service
+
+        include GRPC::GenericService
+
+        self.marshal_class_method = :encode
+        self.unmarshal_class_method = :decode
+        self.service_name = 'grpc.testing.ReconnectService'
+
+        rpc :Start, ReconnectParams, Empty
+        rpc :Stop, Empty, ReconnectInfo
+      end
+
+      Stub = Service.rpc_stub_class
+    end
+  end
+end
diff --git a/src/ruby/qps/src/proto/grpc/testing/services_services.rb b/src/ruby/qps/src/proto/grpc/testing/services_services.rb
index 3fd9f20f4729d4621bed7d4325bb8aa7760df8f7..94b9a1e164e726d7bef4e875eb2b7373a5b722eb 100644
--- a/src/ruby/qps/src/proto/grpc/testing/services_services.rb
+++ b/src/ruby/qps/src/proto/grpc/testing/services_services.rb
@@ -1,5 +1,37 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # Source: src/proto/grpc/testing/services.proto for package 'grpc.testing'
+# Original file comments:
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# An integration test service that covers all the method signature permutations
+# of unary/streaming requests/responses.
 
 require 'grpc'
 require 'src/proto/grpc/testing/services'
@@ -7,8 +39,6 @@ require 'src/proto/grpc/testing/services'
 module Grpc
   module Testing
     module BenchmarkService
-
-      # TODO: add proto service documentation here
       class Service
 
         include GRPC::GenericService
@@ -17,15 +47,17 @@ module Grpc
         self.unmarshal_class_method = :decode
         self.service_name = 'grpc.testing.BenchmarkService'
 
+        # One request followed by one response.
+        # The server returns the client payload as-is.
         rpc :UnaryCall, SimpleRequest, SimpleResponse
+        # One request followed by one response.
+        # The server returns the client payload as-is.
         rpc :StreamingCall, stream(SimpleRequest), stream(SimpleResponse)
       end
 
       Stub = Service.rpc_stub_class
     end
     module WorkerService
-
-      # TODO: add proto service documentation here
       class Service
 
         include GRPC::GenericService
@@ -34,9 +66,23 @@ module Grpc
         self.unmarshal_class_method = :decode
         self.service_name = 'grpc.testing.WorkerService'
 
+        # Start server with specified workload.
+        # First request sent specifies the ServerConfig followed by ServerStatus
+        # response. After that, a "Mark" can be sent anytime to request the latest
+        # stats. Closing the stream will initiate shutdown of the test server
+        # and once the shutdown has finished, the OK status is sent to terminate
+        # this RPC.
         rpc :RunServer, stream(ServerArgs), stream(ServerStatus)
+        # Start client with specified workload.
+        # First request sent specifies the ClientConfig followed by ClientStatus
+        # response. After that, a "Mark" can be sent anytime to request the latest
+        # stats. Closing the stream will initiate shutdown of the test client
+        # and once the shutdown has finished, the OK status is sent to terminate
+        # this RPC.
         rpc :RunClient, stream(ClientArgs), stream(ClientStatus)
+        # Just return the core count - unary call
         rpc :CoreCount, CoreRequest, CoreResponse
+        # Quit this worker
         rpc :QuitWorker, Void, Void
       end