There is no easy way to know exactly what a single key press will do.
If you see additional behavior always check the common hooks. See the list here: http://www.gnu.org/software/emacs/manual/html_node/elisp/Standard-Hooks.html
In most cases the important ones are:
- after-change-functions
- before-change-functions
- first-change-hook
- post-command-hook
- pre-command-hook
- post-self-insert-hook
You'll need to inspect those hooks and look into the functions they contain to see which one is modifying your behavior.
If the functions in these hooks don't fully describe the observed behavior, check the functions for advice which will show up in their documentation from describe-function
.
Edit:
I've written some functions to help describe a hook better than going through the functions one by one: https://gist.github.com/jordonbiondo/bad03e44bb053db0f1eb
You can use describe-hook
defined there like the other describe functions. Here is a sample of it's output:

And here's all the code, in case the gist disappears:
(defun guess-all-hooks ()
"Return a list of all variables that are probably hook lists."
(let ((syms '()))
(mapatoms
(lambda (sym)
(if (ignore-errors (symbol-value sym))
(let ((name (symbol-name sym)))
(when (string-match "-\\(hook[s]?\\|functions\\)$" name)
(push sym syms))))))
syms))
(defun face-it (str face)
"Apply FACE to STR and return."
(propertize str 'face face))
(defun describe-hook (hook)
"Display documentation about a hook variable and the
functions it contains."
(interactive
(list (completing-read
"Hook: " (mapcar (lambda (x) (cons x nil)) (guess-all-hooks)))))
(let* ((sym (intern hook))
(sym-doc (documentation-property sym 'variable-documentation))
(hook-docs (mapcar
(lambda (func)
(cons func (ignore-errors (documentation func))))
(symbol-value sym))))
(switch-to-buffer
(with-current-buffer (get-buffer-create "*describe-hook*")
(let ((inhibit-read-only t))
(delete-region (point-min) (point-max))
(insert (face-it "Hook: " 'font-lock-constant-face) "\n\n")
(insert (face-it (concat "`" hook "'") 'font-lock-variable-name-face))
(replace-string "\n" "\n\t" nil
(point)
(save-excursion
(insert "\n" sym-doc "\n\n")
(1- (point))))
(goto-char (point-max))
(insert (face-it "Hook Functions: " 'font-lock-constant-face) "\n\n")
(dolist (hd hook-docs)
(insert (face-it (concat "`" (symbol-name (car hd)) "'")
'font-lock-function-name-face)
": \n\t")
(replace-string "\n" "\n\t" nil
(point)
(save-excursion
(insert (or (cdr hd) "No Documentation") "\n\n")
(1- (point))))
(goto-char (point-max))))
(help-mode)
(help-make-xrefs)
(read-only-mode t)
(setq truncate-lines nil)
(current-buffer)))))
electric-pair-mode
was activated in only some major modes? Do you still seeself-insert-command
for"
when you doC-h k
whileelectric-pair-mode
is active?electric-pair-mode
works by hooking intopost-self-insert-hook
, and not by changing the keybinding.C-h k
tells you exactly what happens for a keypress. If you look at the documentation forself-insert-command
, it very clearly sayspost-self-insert-hook
is run after the command finishes.after-change-functions
like Jordon mentions in his answer? The documentation for a function probably won't specifically mention that hook, will it?C-h k
+ the standard hooks == the complete behavior (more or less). Of course sometimes that leaves a lot of possibilities, but it's still a lot more transparent than any other complex software system I know of.