diff --git a/doc/guix.texi b/doc/guix.texi
index 333cb4117a..a48fb0e2b7 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -35652,10 +35652,11 @@ them in an @code{operating-system} declaration.  But how do we define
 them in the first place?  And what is a service anyway?
 
 @menu
-* Service Composition::         The model for composing services.
-* Service Types and Services::  Types and services.
-* Service Reference::           API reference.
-* Shepherd Services::           A particular type of service.
+* Service Composition::            The model for composing services.
+* Service Types and Services::     Types and services.
+* Service Reference::              API reference.
+* Shepherd Services::              A particular type of service.
+* Writing Service Configurations:: A guideline for writing guix services.
 @end menu
 
 @node Service Composition
@@ -35851,6 +35852,206 @@ There can be only one instance of an extensible service type such as
 Still here?  The next section provides a reference of the programming
 interface for services.
 
+@node Writing Service Configurations
+@subsection Writing Service Configurations
+
+There are a lot of system and home services already written, but from
+time to time it's necessary to write one more.  This section contains
+tips for simplifying this process, and should help to make service
+configurations and their implementations more consistent.
+
+@quotation Note
+If you find any exceptions or patterns missing in this section, please
+send a patch with additions/changes to @email{guix-devel@@gnu.org}
+mailing list or just start a discussion/ask a question.
+@end quotation
+
+@subsubheading Configuration Itself
+
+As we know from previous section a guix service can accept a value and
+be extended with additional values by other services.  There are some
+cases, when the service accepts a list of pairs or some other values for
+example @code{console-font-service-type} accepts list of pairs (tty and
+font name/file) or @code{etc-service-type} accepts list of lists
+(resulting file name and file-like object), those services are kinda
+special, they are an intermediate helpers doing auxiliary work.
+
+However, in most cases a guix service is wrapping some software, which
+consist of package or a few packages, and configuration file or files.
+Therefore, the value for such service is quite complicated and it's hard
+to represent it with a just list or basic data type, in such cases we
+use a record.  Each such record have -configuration suffix, for example
+@code{docker-configuration} for @code{docker-service-type} and a few
+different fields helping to customize the software.  Configuration
+records for home services also have a @code{home-} prefix in their name.
+
+There is a module @code{gnu service configuration}, which contains
+helpers simplifying configuration definition process.  Take a look at
+@code{gnu services docker} module or grep for
+@code{define-configuration} to find usage examples.
+
+@c Provide some examples, tips, and rationale behind @code{gnu service
+@c configuration} module.
+
+After a configuration record properly named and defined let's discuss
+how to name and define fields, and which approach to use for
+implementing the serialization code related to them.
+
+@subsubheading Configuration Record Fields
+
+@enumerate
+@item
+It's a good idea to have a field/fields for specifying package/packages
+being installed for this service.  For example
+@code{docker-configuration} has @code{docker}, @code{docker-cli},
+@code{containerd} fields.  Sometimes it make sense to make a field,
+which accepts a list of packages for cases, where an arbitrary list of
+plugins can be passed to the configuration.  There are some services,
+which provide a field called @code{package} in their configuration,
+which is ok, but the way it done in @code{docker-configuration} is more
+flexible and thus preferable.
+
+@item
+Fields for configuration files, should be called the same as target
+configuration file name, but in kebab-case: bashrc for bashrc,
+bash-profile for bash_profile, etc.  The implementation for such fields
+will be discussed in the next subsubsection.
+
+@item
+Other fields in most cases add some boilerplates/reasonable defaults to
+configuration files, turns on/off installation of some packages or
+provide other custom behavior.  There is no any special requirements or
+recommendations here, but it's necessary to make it possible to disable
+all the effects of such fields to provide a user with an empty
+configuration and let them generate it from scratch with only field for
+configuration file.
+
+@end enumerate
+
+@subsubheading Fields for Configuration Files
+
+The field should accept a datastructure (preferably a combination of
+simple lists, alists, vectors, gexps and basic data types), which will
+be serialized to target configuration format, in other words it should
+provide an alternative lisp syntax, which can be later translated to
+target one, like SXML for XML.  Such approach is quite flexible and
+simple, it requires to write serializer once for one configuration
+format and can be reused multiple times in different guix services.
+
+Let's take a look at JSON: we implement serialization function, which
+converts vectors to arrays, alists to objects (AKA dictionaries or
+associative arrays), numbers to numbers, gexps to the strings, file-like
+objects to the strings, which contains the path to the file in the
+store, @code{#t} to @code{true} and so on, and now we have all apps
+using JSON and YAML as a format for configurations covered.  Maybe some
+fine-tunning will be needed for particular application, but the primary
+serilalization part is already finished.
+
+The pros and cons of such approach is inherited from open-world
+assumption.  It doesn't matter if underlying applications provides new
+configuration options, we don't need to change anything in service
+configuration and its serialization code, it will work perfectly fine,
+on the other hand it harder to type check and structure check
+``compile-time'', and we can end up with configuration, which won't be
+accepted by target application cause of unexisting, misspelled or
+wrongly-typed options.  It's possible to add those checks, but we will
+get the drawbacks of closed-world assumption: we need to keep the
+service implementation in-sync with app config options, and it will make
+impossible to use the same service with older/newer package version,
+which has a slightly different list of available options and will add an
+excessive maintanence load.
+
+However, for some applications with really stable configuration those
+checks can be helpful and should be implemented if possible, for some
+other we can implement them only partially.
+
+The alternative approach applied in some exitsting services is to use
+records for defining the structure of configuration field, it has the
+same downsides of closed-world assumption and a few more problems:
+
+@enumerate
+@item
+It has to replicate all the available options for the app (sometimes
+hundreds or thousands) to allow user express any configuration they
+wants.
+@item
+Having a few records, adds one more layer of abstraction between service
+configuration and resulting app config, including different field
+casing, new semantic units.
+@c provide examples?
+@item
+It harder to implement optional settings, serialization becomes very
+ad-hoc and hard to reuse among other services with the same target
+config format.
+@end enumerate
+
+Exceptions can exist, but the overall idea is to provide a lispy syntax
+for target configuration.  Take a look at sway example configuration
+(which also can be used for i3).  The following value of @code{config}
+field of @code{home-sway-configuration}:
+
+@example
+`((include ,(local-file "./sway/config"))
+  (bindsym $mod+Ctrl+Shift+a exec emacsclient -c --eval "'(eshell)'")
+  (bindsym $mod+Ctrl+Shift+o "[class=\"IceCat\"]" kill)
+  (input * ((xkb_layout us,ru)
+            (xkb_variant dvorak,))))
+@end example
+
+would yield something like:
+
+@example
+include /gnu/store/408jwvh6wxxn1j85lj95fniih05gx5xj-config
+bindsym $mod+Ctrl+Shift+a exec emacsclient -c --eval '(eshell)'
+bindsym $mod+Ctrl+Shift+o [class="IceCat"] kill
+input * @{
+    xkb_layout us,ru
+    xkb_variant dvorak,
+@}
+@end example
+
+The mapping between scheme code and resulting configuration is quite
+obvious.  The serialization code with some type and structure checks
+takes less than 70 lines and every possible sway/i3 configuration can be
+expressed using this field.
+
+@subsubheading Let User Escape
+Sometimes user already have a configuration file for an app, make sure
+that it is possible to reuse it directly without rewriting.  In the
+example above, the following snippet allows to include already existing
+config to the newly generated one utilizing @code{include} directive of
+i3/sway config language:
+
+@example
+(include ,(local-file "./sway/config"))
+@end example
+
+When building a resulting config the file-like objects are substituted
+with a path of the file in the store and sway's @code{include} loads
+this file during startup.  The way file-like objects are treated here
+also allows to specify paths to plugins or other binary files like:
+@code{(load-plugin ,(file-append plugin-package "/share/plugin.so"))}
+(the example value for imaginary service configuration config file
+field).
+
+In some cases target configuration language may not have such
+@code{include} directive and can't provide such a functionallity, to
+workaround it we can do the following trick:
+
+@example
+`(#~(call-with-input-file
+     #$(local-file "./sway/config")
+     (@@ (ice-9 textual-ports) get-string-all)))
+@end example
+
+G-expressions get serialized to its values, and the example above reads
+the content of the file-like object and inserts it in the resulting
+configuration file.
+
+Following these simple rules will help to make a simple, consistent and
+maintainable service configurations, will let user express any possible
+needs and reuse existing configuration files.
+
 @node Service Reference
 @subsection Service Reference
 
